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;
}
原文地址:https://www.cnblogs.com/xyq0220/p/13843381.html