Codeforces 1428F Fruit Sequences 乱搞计数
Codeforces 1428F Fruit Sequences
题意
给一个长度为\(n\)的\(01\)串\(s\),\(f(l,r)\)为区间\([l,r]\)内最长连续\(1\)的个数,计算\(\sum_{i=1}^{n}\sum_{j=i}^{n}f(i,j)\)。
\(n \le 5 \cdot 10^5\)
分析
设\(pre[i]\)为位置\(i\)之前连续\(1\)的个数,\(suf[i]\)为位置\(i\)之后连续\(1\)的个数,例如\(00111100\),\(pre[4]=2,suf[4]=3\)。
令\(dp[i]\)为\(\sum_{j=1}^{i}f(j,i+suf[i]-1)\),如何计算\(dp[i]\):
- 首先要加上\(\sum_{j=i-pre[i]+1}^{i}suf[j]\),也就是包含位置\(i\)的一段连续\(1\)的贡献,可以用求和公式化简。
- 再找到\(i\)之前最近的一个位置\(j\),且\(suf[j]>pre[i]+suf[i]-1\),若找不到则令\(j=0\),加上\(dp[j]+(i-pre[i]-j)\cdot (pre[i]+suf[i]-1)\)。
计算完\(dp\)数组后,再枚举端点\(r\),令\(ans\)加上\(\sum_{j=1}^{pre[i]}j\),也就是\(r\)之前连续一段\(1\)的贡献,再类似的找到\(r\)之前最近的一个位置\(j\),且\(suf[j]>pre[r]\),若找不到则令\(j=0\),加上\(dp[j]+(r-pre[r]-j)\cdot pre[r]\)。
Code
#include <bits/stdc++.h>
#define lson l,mid,p<<1
#define rson mid+1,r,p<<1|1
using namespace std;
typedef long long ll;
const int N=5e5+10;
int n;
int a[N];
ll f[N];
int pre[N],suf[N],st[N][20];
void init(){
for(int i=1;i<=n;i++) st[i][0]=suf[i];
for(int j=1;(1<<j)<=n;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
}
int qmx(int l,int r){
int k=(int)log2(r-l+1);
return max(st[l][k],st[r-(1<<k)+1][k]);
}
int qy(int x,int num){
int l=1,r=x;
while(l<=r){
int mid=l+r>>1;
int cnt=qmx(mid,x);
if(cnt>=num) l=mid+1;
else r=mid-1;
}
return r;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%1d",&a[i]);
for(int i=1;i<=n;i++){
if(a[i]) pre[i]=pre[i-1]+1;
}
for(int i=n;i>=1;i--){
if(a[i]) suf[i]=suf[i+1]+1;
}
init();
for(int i=1;i<=n;i++) if(a[i]){
f[i]=1ll*(2*suf[i]+pre[i]-1)*pre[i]/2;
int x=pre[i]+suf[i]-1;
int j=qy(i-pre[i],x+1);
f[i]+=f[j];
f[i]+=1ll*(i-pre[i]-j)*x;
}
ll ans=0;
for(int i=1;i<=n;i++){
ans+=1ll*pre[i]*(pre[i]+1)/2;
int j=qy(i-pre[i],pre[i]+1);
ans+=f[j];
ans+=1ll*(i-pre[i]-j)*pre[i];
}
cout<<ans<<endl;
return 0;
}