CF2121G补

题意:给一个二进制串,对于每一个区间,对1的个数和0的个数取max,将这个max值求和输出。

Sol:(还是好难想)首先$$max(x,y)=\frac{x+y+|x-y|}{2}$$,这里的x是区间内0的个数,y是区间内1的个数,那么x+y即代表区间长度,则\(an1\)

\[\sum_{i=1}^ni\times(n-i+1)=(n+1)\sum_{i=1}^ni-\sum_{i=1}^ni^2=\frac{(n+1)^2n}{2}-\frac{n(n+1)(2n+1)}{6}=\frac{n(n+1)(n+2)}{6} \]

\(|x-y|\)这部分,记\(f_i\)为前\(i\)个数中 1 减去 0 的个数,则\(an2\)为:

\[\sum_{l=0}^n\sum_{r=l}^n|f_r-f_l|=\sum_{i=0}^ng_i\times(2i-n) \]

首先一定要注意\(l\)\(0\)开始
\(g_i\)是升序排序的\(f_i\),这样排好序后,我们直接统计每个\(g_i\)贡献的次数,由于每种组合只有一遍,所以只统计一遍,故我们不妨都让\(g_i\)是l的时候来统计一遍;
\([0,n]\)的遍历中,第i位\(g_i\)比它小的有\(i\)个,此时绝对值里面为负,\(g_i\)所贡献的是\(g_i\);第\(i\)\(g_i\)比它大的有\(n-i\)个,此时绝对值里面为正,\(g_i\)所贡献的是\(-g_i\);

这种优化方式应该很有用

#include<bits/stdc++.h>
using namespace std;
#define in read()
#define int long long
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define lowbit(x) (x&-x)
#define pi acos(-1)
inline int read(){
	int f=1,k=0;
	char cp=getchar();
	while(cp!='-'&&(cp<'0'||cp>'9')) cp=getchar();
	if(cp=='-') f=-1,cp=getchar();
	while(cp>='0'&&cp<='9') k=(k<<3)+(k<<1)+cp-48,cp=getchar();
	return f*k;
}
const int N = 5e5+10;
struct Edge{
	int u,v,w,nxt;
}e[N<<1];
int n,first[N],dis[N],ccc,m,vis[N],T=1,f[N];
//void add(int u, int v, int w=1) {e[++ccc]=(Edge){u,v,w,first[u]};first[u]=ccc;}
int gcd(int a,int b) {
    return b>0 ? gcd(b,a%b):a;
}
char s[N];
void solve() {
	cin>>n;
	scanf("%s",s+1);
	int an1=0,an2=0;
	for(int i=1;i<=n;i++){
		an1+=i*(n+1-i);
	}
	f[0]=0;
	for(int i=1;i<=n;i++){
		f[i]=(s[i]=='1'?f[i-1]+1:f[i-1]-1);
	}
	sort(f,f+1+n);
	for(int i=0;i<=n;i++){
		an2+=f[i]*(2*i-n);
	}
	cout<<(an1+an2>>1)<<"\n";
}
signed main() {
//	ios::sync_with_stdio(false);cin.tie(0);
	T=in;
	while(T--) {
		solve();
	}
	return 0;
}
//-Wall -Wextra -std=c++11
posted @ 2025-07-04 17:07  一张十万元  阅读(20)  评论(0)    收藏  举报