二进制数能被3整除相当于奇数、偶数位上1的个数模3同余。那么如果有偶数个1,一定存在重排方案使其合法;否则则要求至少有两个0且至少有3个1,这样可以给奇数位单独安排3个1。
考虑线段树维护区间内的一堆东西,合并两节点时计算跨过区间中点的答案。可以对每个节点记录f[0/1][0/1][0/1][0/1/2]表示前/后缀,异或和为0/1,是否至少出现了两个1,出现了0/1/超过2个0。大力讨论即可。
成功写了一晚上才不是因为要补十几面数学作业
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 100010 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,a[N]; struct data{int f[2][2][2][3],l,r,sum;ll ans; }tree[N<<2]; data merge(data x,data y) { data p;memset(p.f,0,sizeof(p.f)); p.l=x.l,p.r=y.r; p.sum=x.sum+y.sum; p.ans=x.ans+y.ans; for (int i=0;i<2;i++) { int s1=0,s2=0; for (int j=0;j<2;j++) for (int k=0;k<3;k++) s1+=x.f[1][i][j][k],s2+=y.f[0][i][j][k]; p.ans+=1ll*s1*s2; } for (int i=0;i<3;i++) for (int j=2-i;j<3;j++) for (int u=0;u<2;u++) for (int v=0;v<2;v++) if (u|v) p.ans+=1ll*x.f[1][0][u][i]*y.f[0][1][v][j]+1ll*x.f[1][1][u][i]*y.f[0][0][v][j]; int lone=x.sum,lzero=x.r-x.l+1-lone,rone=y.sum,rzero=y.r-y.l+1-rone; for (int i=0;i<2;i++) for (int j=0;j<2;j++) for (int k=0;k<3;k++) p.f[0][i][j][k]+=x.f[0][i][j][k],p.f[0][i^(lone&1)][lone+i>=2||j][min(2,lzero+k)]+=y.f[0][i][j][k], p.f[1][i][j][k]+=y.f[1][i][j][k],p.f[1][i^(rone&1)][rone+i>=2||j][min(2,rzero+k)]+=x.f[1][i][j][k]; return p; } void newpoint(int k,int x) { memset(tree[k].f,0,sizeof(tree[k].f)); tree[k].f[0][x][0][x^1]=tree[k].f[1][x][0][x^1]=1;tree[k].ans=x^1;tree[k].sum=x; } void build(int k,int l,int r) { tree[k].l=l,tree[k].r=r; if (l==r){newpoint(k,a[l]);return;} int mid=l+r>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); tree[k]=merge(tree[k<<1],tree[k<<1|1]); } void modify(int k,int p,int x) { if (tree[k].l==tree[k].r) {newpoint(k,x);return;} int mid=tree[k].l+tree[k].r>>1; if (p<=mid) modify(k<<1,p,x); else modify(k<<1|1,p,x); tree[k]=merge(tree[k<<1],tree[k<<1|1]); } data query(int k,int l,int r) { if (tree[k].l==l&&tree[k].r==r) return tree[k]; int mid=tree[k].l+tree[k].r>>1; if (r<=mid) return query(k<<1,l,r); else if (l>mid) return query(k<<1|1,l,r); else return merge(query(k<<1,l,mid),query(k<<1|1,mid+1,r)); } int main() { #ifndef ONLINE_JUDGE freopen("bzoj5294.in","r",stdin); freopen("bzoj5294.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(); for (int i=1;i<=n;i++) a[i]=read(); build(1,0,n); m=read(); while (m--) { int op=read(); if (op==1) { int x=read(); modify(1,x,a[x]^=1); } else { int l=read(),r=read(); printf(LL,query(1,l,r).ans); } } return 0; }