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
本文来自博客园,作者:一张十万元,转载请注明原文链接:https://www.cnblogs.com/Jason--Yang/p/18965912

浙公网安备 33010602011771号