https://codeforces.com/gym/323862/problem/A
————————————————————————————————————
1111111==>11101111
添加的0会对组数造成增多的影响
如果中心1在0左边,则会使右边的连串0对左侧1造成影响(增多0的个数种选择数)
反之亦然
————————————————————————————————————
思路:
开数组记录每个1前面0的个数L,与每个1后面0的个数R,
对于第i个位置:种类数L(i)xR(i)+∑(L(i-a)+1)x(R(i-a)+1)
然而这样会超时
————————————————————————————————————
优化:
把L与R全部+1
发现奇与奇相乘,偶与偶相同。
对于一个七个1的序列:
L1x(R3+R5+R7)+
L3x(R5+R7)+
L5x(R7)
偶数同理
所以维护一个后缀和
取L[i] x sum[i+2]
#include<bits/stdc++.h>
using namespace std;
long long l[200500],r[200500]; //1左侧0的数量与右侧0的数量
long long s[200500]; //奇偶前缀和
void ope(){
long long ans=0;
int n;
int j,o;//奇偶右端点
int num=1;//1的总个数
char t;
cin>>n;
memset(l,0,sizeof l);
memset(r,0,sizeof r);
memset(s,0,sizeof s);
for(int i=0;i<n;i++){ //记录左侧1的个数
cin>>t;
if(t=='0'){
l[num]++;
}else{
num++;
}
}
num--;
for(int i=0;i<=num;i++){ //这里一定要从0开始,不然l[1]不会+1,答案会错
l[i+1]++;
r[i]=l[i+1];
}
for(int i=1;i<=num;i++){ //先把只有自己一个1的情况算进去
ans+=(l[i]-1)*(r[i]-1);
}
for(int i=num;i>0;i--){ //处理一个后缀和
s[i]=s[i+2]+r[i];
}
for(int i=1;i<=num;i++){ //左侧乘右侧的后缀和
ans+=l[i]*s[i+2];
}
cout<<ans<<endl;
return;
}
int main(){
int n;
cin>>n;
while(n--){
ope();
}
return 0;
}