一、题目
二、解法
首先研究什么样的子串是可重排的,可以把二进制位理解成给 \(0\) 或者 \(1\) 一个权重。如果是奇数位置(从低往高)则权重为正一,如果是偶数位置则权重为负一,要求所有 \(1\) 的权重之和是 \(3\) 的倍数。
考虑如果 \(1\) 的个数为偶数,可以直接正一负一相抵消,一定存在解。如果 \(1\) 的个数为大于 \(1\) 的奇数,考虑构造三个同奇偶的位置也可以让权重之和是 \(3\) 的倍数,若总长度是偶数,则要求 \(0\) 的个数大于 \(1\);若总长度是奇数,则要求存在 \(0\)
可重排的情况太复杂了,考虑计算不可重排的情况。根据上面的分析,可以写出不可重排的充要条件是:
- \(1\) 的个数是 \(1\) 且 \(0\) 的个数 \(\geq 2\)
- \(1\) 的个数为奇数且 \(0\) 的个数 \(<2\)
直接上线段树维护这东西,上面两部分可以分别计算:
-
对于第一部分,维护 \(l_0,l_1,r_0,r_1\) 分别表示 \(1\) 的个数为 \(0/1\),这样的前缀\(/\)后缀有多少个。
-
对于第二部分,维护 \(L[i][j]\)(\(i,j\in[0,1]\))表示有多少个前缀 \(0\) 的个数为 \(i\),\(1\) 的奇偶性为 \(j\);\(R[i][j]\) 表示这样的后缀有多少个。
上述的信息都是易于合并的,拿他们计算答案也很容易。一个小细节是合并时中间 \(10/01\) 的情况可能会多算一次,注意去重。时间复杂度 \(O(n\log n)\)
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 100005;
#define int long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,mid,z[M];
struct node
{
int l0,l1,r0,r1,c0,c1;
int l[2][2],r[2][2],res;
node()
{
l0=l1=r0=r1=c0=c1=res=0;
memset(l,0,sizeof l);memset(r,0,sizeof r);
}
void init(int x)
{
(*this)=node();
if(x) l1=r1=c1=l[0][1]=r[0][1]=res=1;
else l0=r0=c0=l[1][0]=r[1][0]=1;
}
}t[M<<2];
node merge(node a,node b,int mid)
{
node x;
x.c0=a.c0+b.c0;x.c1=a.c1+b.c1;
x.l0=a.l0+(!a.c1?b.l0:0);
x.r0=b.r0+(!b.c1?a.r0:0);
x.l1=a.l1+(!a.c1?b.l1:0)+(a.c1==1?b.l0:0);
x.r1=b.r1+(!b.c1?a.r1:0)+(b.c1==1?a.r0:0);
//
for(int i=0;i<2;i++) for(int j=0;j<2;j++)
{
x.l[i][j]=a.l[i][j]+(i>=a.c0?b.l[i-a.c0][j^(a.c1&1)]:0);
x.r[i][j]=b.r[i][j]+(i>=b.c0?a.r[i-b.c0][j^(b.c1&1)]:0);
}
//
x.res=a.res+b.res;
x.res+=a.r0*b.l1+a.r1*b.l0;
x.res+=a.r[0][0]*(b.l[0][1]+b.l[1][1]);
x.res+=a.r[0][1]*(b.l[0][0]+b.l[1][0]);
x.res+=a.r[1][0]*b.l[0][1]+a.r[1][1]*b.l[0][0];
//
if(z[mid]+z[mid+1]==1) x.res--;
return x;
}
void build(int i,int l,int r)
{
if(l==r) {t[i].init(z[l]);return ;}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
t[i]=merge(t[i<<1],t[i<<1|1],mid);
}
void upd(int i,int l,int r,int x)
{
if(l==r) {t[i].init(z[l]);return ;}
int mid=(l+r)>>1;
if(mid>=x) upd(i<<1,l,mid,x);
else upd(i<<1|1,mid+1,r,x);
t[i]=merge(t[i<<1],t[i<<1|1],mid);
}
node ask(int i,int l,int r,int L,int R)
{
if(L<=l && r<=R) return t[i];
int mid=(l+r)>>1;
if(R<=mid) return ask(i<<1,l,mid,L,R);
if(mid<L) return ask(i<<1|1,mid+1,r,L,R);
return merge(ask(i<<1,l,mid,L,R),
ask(i<<1|1,mid+1,r,L,R),mid);
}
signed main()
{
n=read();
for(int i=1;i<=n;i++) z[i]=read();
build(1,1,n);
m=read();
for(int i=1;i<=m;i++)
{
int op=read(),l=read();
if(op==1) z[l]^=1,upd(1,1,n,l);
if(op==2)
{
int r=read(),sum=(r-l+1)*(r-l+2)/2;
printf("%lld\n",sum-ask(1,1,n,l,r).res);
}
}
}