水题。
假设有两个二进制数a,b,c=a+b(a,b拼接起来)
那么显然如果b长度为偶数(cmod 3=(bmod 3+amod 3)mod 3)
否则(cmod 3=(bmod 3+(amod 3)*2)mod 3)
那么只要记一个区间的前缀和后缀就行了,合并的时候左儿子的后缀和右儿子的前缀合并。
具体见代码
#include<bits/stdc++.h>
#define il inline
#define vd void
typedef long long ll;
il int gi(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
int a[500010];
struct yyb{
bool len;
ll l[3][2],r[3],ans,num;
//len表示长度是奇数/偶数
//l[i][j]表示长度为奇数/偶数膜3余0/1/2的前缀数量
//r[i]表示膜3余0/1/2的前缀数量
//ans表示答案
//num表示这个区间膜3
}s[500010<<2];
il yyb operator +(const yyb&a,const yyb&b){
yyb c;
c.len=a.len^b.len;
for(int i=0;i<3;++i)c.l[i][0]=a.l[i][0],c.l[i][1]=a.l[i][1],c.r[i]=b.r[i];
c.ans=a.ans+b.ans;
for(int i=0;i<3;++i)//答案加上中间部分
for(int j=0;j<3;++j)
for(int k=0;k<2;++k)
if((i*(k?2:1)+j)%3==0)c.ans+=a.r[i]*b.l[j][k];
for(int i=0;i<3;++i)
for(int j=0;j<2;++j)
c.l[(a.num*(j?2:1)+i)%3][j^a.len]+=b.l[i][j];
for(int i=0;i<3;++i)c.r[(i*(b.len?2:1)+b.num)%3]+=a.r[i];
c.num=(a.num*(b.len?2:1)+b.num)%3;
return c;
}
#define mid ((l+r)>>1)
il vd set(int x,int p){
s[x].len=1;
memset(s[x].l,0,sizeof s[x].l);
memset(s[x].r,0,sizeof s[x].r);
s[x].ans=!a[p];s[x].num=a[p];
s[x].l[a[p]][1]=s[x].r[a[p]]=1;
}
il vd build(int x,int l,int r){
if(l==r){set(x,l);return;}
build(x<<1,l,mid),build(x<<1|1,mid+1,r);
s[x]=s[x<<1]+s[x<<1|1];
}
il vd update(int x,int l,int r,const int&p){
if(l==r){set(x,l);return;}
if(p<=mid)update(x<<1,l,mid,p);
else update(x<<1|1,mid+1,r,p);
s[x]=s[x<<1]+s[x<<1|1];
}
il yyb query(int x,int l,int r,const int&L,const int&R){
if(L<=l&&r<=R)return s[x];
if(L<=mid)
if(mid<R)return query(x<<1,l,mid,L,R)+query(x<<1|1,mid+1,r,L,R);
else return query(x<<1,l,mid,L,R);
else return query(x<<1|1,mid+1,r,L,R);
}
int main(){
int n=gi(),m=gi();
for(int i=1;i<=n;++i)a[i]=gi();
build(1,1,n);
int o,l,r;
while(m--){
o=gi(),l=gi();
if(o==1)a[l]^=1,update(1,1,n,l);
else r=gi(),printf("%lld
",query(1,1,n,l,r).ans);
}
return 0;
}