G - Gangsta
题意:给定一个长度为n的01串,记f(l,r) 为出现区间[l,r]最多次字符的出现次数,求和sigma 1<=i<=j<=n f(j,i)
思路:
先考虑f(l,r)怎么求,设x为出现0的次数,y为出现1的次数
f(l,r)=max(x,y) =( x+y+|x-y| )/2
显然对于01串,x+y为区间长度len
关于len的求和:显然每个长度为i的区间总个数为(n-i+1) , 那么为 sigma求和 1<=i<=n ix(n-i+1)
关于ixi的求和公式:n(n+1)(2n+1)/6
关于|x-y|的求和,|x-y|的值是区间0和1的个数的差值
不妨令数组值为0的位置权值为-1,值为1的位置权值为1
那么|x-y|=|pre[r]-pre[l-1]|,即0<=l<r<=n sigma求和 |pre[r]-pre[l]|
由于对于这种二元关系而言,改变次序不会改变二元组的双方与对数以及结果的绝对值,因此直接将pre排序
对于每一个值,算贡献:设当前pre的位置为i,那么 +的贡献共有i个 , -的贡献共有n-i个,因此它的贡献为(2i-n)xpre_i
求和即可
void solve(){
int n;cin>>n;
string s;cin>>s;
s=" "+s;
vector<int>pre(n+1);
rep(i,1,n){
if(s[i]=='0')pre[i]=-1;
else pre[i]=1;
}
rep(i,1,n){
pre[i]+=pre[i-1];
}
sort(pre.begin(),pre.end());
int ans=0;
rep(i,0,n){
ans+=(2*i-n)*pre[i];
}
ans+=n*(n+1)*(n+2)/6;
cout<<(ans>>1)<<endl;
}

浙公网安备 33010602011771号