简单的平衡规划题目。
注意到“倍数”的限制,这很平衡规划。我们设限制为 $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; }
|