Codeforces 1270 F. Awesome Substrings —— 根據資料大小使用不同方法
阿新 • • 發佈:2020-12-15
技術標籤:想法
題意:
給你一個01串,問你有多少子串,它包含的1的個數是它長度的因子。
題解:
這種題目想不出來的時候應該列一下公式啊,設sum[i]表示到了第i個位置的時候,有多少個1.
那麼就是要我們求
i
−
(
j
−
1
)
=
(
s
u
m
[
i
]
−
s
u
m
[
j
−
1
]
)
∗
x
i-(j-1)=(sum[i]-sum[j-1])*x
i−(j−1)=(sum[i]−sum[j−1])∗x這裡有三個變數:i,j,x。
i和j就表示這個子串的末尾和起始位置,x就表示長度是1個數的多少倍
然後我們可以知道
(
s
u
m
[
i
]
−
s
u
m
[
j
−
1
]
)
∗
x
<
=
n
(sum[i]-sum[j-1])*x<=n
(sum[i]−sum[j−1])∗x<=n
於是對於1的個數和x的值我們可以分開考慮。
首先是列舉1的個數的情況i,我們同時列舉左端點的位置l,然後對於左端點右邊第i個1和第i+1個1這個區間直接算出有多少種情況加到答案裡即可:
然後是列舉乘數的情況,首先要確定一點:假設我們列舉1的個數是從1列舉到m,那麼此時運算的時候1的個數一定要大於m:
繼續推上面那個公式
i
−
s
u
m
[
i
]
∗
x
=
(
j
−
1
)
−
s
u
m
[
j
−
1
]
∗
x
i-sum[i]*x=(j-1)-sum[j-1]*x
i−sum[i]∗x=(j−1)−sum[j−1]∗x
那麼我們直接列舉右端點,檢視有多少左端點合法,我這裡偷懶了用unordered_map來記,因此這個時候m的取值必須要較大,我這裡取的是2000,也就是讓第一種情況畫的時間較多一點,第二種情況由於有個小log,所以其它時間花的要較少一點。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+5;
char s[N];
vector<int>p1;
int sum[N*2];
unordered_map<int,int>num;
int main()
{
int n;
scanf("%s",s+1);
n=strlen(s+1);
int m=min(n,2000);
for(int i=1;i<=n;i++){
if (s[i]=='1')
p1.push_back(i);
sum[i]=sum[i-1]+(s[i]=='1');
}
p1.push_back(n+1);
int sz=p1.size();
ll ans=0;
for(int i=1;i<=m&&i<sz;i++){
int l=1,r=i;
for(;l<=n;l++){
ans+=(p1[r]-l)/i-(p1[r-1]-l)/i;
if(s[l]=='1')r++;
if(r>=sz)break;
}
}
if(m>=sz)return 0*printf("%lld\n",ans);
for(int k=1;k<=n/m;k++){
num.clear();
int r=m,l=0,pl=1,v;
num[0]=1;
for(int i=p1[m];i<=n;i++){
r+=s[i]=='1';
while(sum[i]-sum[pl]>m){
l+=s[pl]=='1';
v=pl-l*k;
num[v]++;
pl++;
}
v=i-r*k;
ans+=num[v];
}
}
printf("%lld\n",ans);
return 0;
}