Liveddd's Blog

愛にできることはまだあるかい

CF1270F Awesome Substrings

简单的平衡规划题目。

注意到“倍数”的限制,这很平衡规划。我们设限制为 $B$。若直接枚举 $k\le B$,条件为 $k(s_r-s_l)=r-l$,条件即为 $k\cdot s_l-l=k\cdot s_r-r$,开个桶记录一下,再扫一遍即可。这个做法时间复杂度为 $\mathcal O(nB)$。这是对于 $k$ 较小的做法。

考虑 $k>B$ 的答案,直接暴力枚举起点 $s$,将 $1$ 的点串在一起,这样对于每个 $s$ 最多会跳 $\mathcal O(\frac{n}{B})$ 次。过程中我们直接统计长度为 $k$ 的倍数的个数。

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
const int N=2e5+10,M=1e7+10;
template<class T>
inline void read(T &x)
{
x=0;bool f=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(f)x=~x+1;
}
template<class T,class ...T1>
inline void read(T &x,T1 &...x1)
{
read(x),read(x1...);
}
int n,B;
char s[N];
int pre[N],sum[M<<1];
int ne[N];
ll ans;
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
B=sqrt(n)+1;
s[0]='0';
for(int i=1;i<=n;i++)pre[i]=pre[i-1]+(s[i]=='1');
for(int i=1;i<=B;i++)
{
memset(sum,0,sizeof(sum));
for(int j=0;j<=n;j++)
{
int now=i*pre[j]-j+M;
ans+=sum[now];
sum[now]++;
}
}
int pos=n+1;
for(int i=n;~i;i--)
{
ne[i]=pos;
if(s[i]=='1')pos=i;
}
for(int i=0;i<n;i++)
{
int l=i,r=ne[i];
for(int j=1;j<=n/B&&j<=pre[n]-pre[i];j++)
{
l=ne[l],r=ne[r];
int resl=max(l-i-1,j*B),resr=r-i-1;
if(resl<resr)ans+=resr/j-resl/j;
}
}
printf("%lld",ans);
return 0;
}