BZOJ传送门
洛谷传送门
解析:
我太菜了,orz了yyb的题解才会这道题。
仿照十进制下 整除的数的特点,我们可以知道二进制下被 整除的数的特点。
由于二进制下每一位被三整除的余数是 排列的,其实也就是 的排列。
所以规律还是这个,二进制下奇偶数码和之差能够被 整除的,原数能够被 整除。
那么现在考虑什么样的区间重排之后能够被 整除。
- 偶数个 ,这种区间只需要把所有 全部扔到开头,就能够直接使剩下的 在奇数位置和偶数位置分别占一半,差为 。
-
的个数是不少于
个的奇数,且
不少于
个。
其实这个还是挺好想的,如果只有一个 是肯定不行的。如果有不少于 个 ,我们需要摆出 ,然后剩下的偶数个 摆成连续的就行了。
感觉直接统计合法区间不是什么方便的事,反着统计不合法的区间:
- 一个 ,且 的个数大于等于 个。
- 奇数个 ,且 的个数少于 个。
注意这里为了防止重复统计,在第一条里面增加了限制。
现在考虑合并两个区间的同时,求出横跨了区间分界点的非法区间。
设 表示强制选择当前区间左端点的所有序列中, 出现了 次,且 出现次数的奇偶性为 时的序列数。 同理
设 表示强制经过当前区间左端点的所有序列中, 出现了恰好 次, 出现了 的序列数。 同理
设 表示从当前区间的左端点开始的连续的 的个数。 同理
设 表示当前区间的总的 的个数, 同理。
就是当前区间所有非法子序列的总数。
合并成新的区间:
的转移都十分显然。
的转移判断一下左/右区间是否为全零就能轻易解决了
需要先从当前合并的左右区间继承一下,然后根据左右区间 的个数是 的进行讨论。为 的时候直接跨区间加一下就行了。如果左/右区间刚好有一个 ,那么需要讨论跨区间的转移,但是如果另一个区间分界线处没有连续的 就只能咕咕咕了。
的转移也是同理,先从左右区间继承,发现 不够的时候再考虑跨区间的转移。
的处理其实就是写起来吓人想起来sb。将左区间的 和右区间的 拼成新的转移,只需要保证新的区间 的个数为奇数且没有达到两个 就处理完情况 了。
然后考虑跨中间的情况 ,显然需要两边在区间分界线上有连续的 的时候才能转移,所有的 都能够和另一边的 拼成新的区间,但是要考虑拼出来的 不超过 的情况,需要丢掉。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re char c;
while(!isdigit(c=gc()));re int num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
}
using namespace IO;
struct node{
ll sum;
int l0,r0;
int fl[3],fr[3];
int tot1,tot0;
int dl[2][2],dr[2][2];
void init(){
memset(dl,0,sizeof dl);
memset(dr,0,sizeof dr);
memset(fl,0,sizeof fl);
memset(fr,0,sizeof fr);
sum=tot1=tot0=l0=r0=0;
}
node(){}
node(int x){
init();
if(x)dl[0][1]=dr[0][1]=sum=tot1=fl[0]=fr[0]=1;
else dl[1][0]=dr[1][0]=tot0=l0=r0=1;
}
friend node operator+(cs node &L,cs node &R){
node res;res.init();
for(int re i=0;i<2;++i)
for(int re j=0;j<2;++j){
res.dl[i][j]+=L.dl[i][j];
res.dr[i][j]+=R.dr[i][j];
if(L.tot0<=i)res.dl[i][j]+=R.dl[i-L.tot0][j^(L.tot1&1)];
if(R.tot0<=i)res.dr[i][j]+=L.dr[i-R.tot0][j^(R.tot1&1)];
}
for(int re i=0;i<3;++i){
res.fl[i]+=L.fl[i];
res.fr[i]+=R.fr[i];
if(L.tot1==0)res.fl[min(2,i+L.tot0)]+=R.fl[i];
if(R.tot1==0)res.fr[min(2,i+R.tot0)]+=L.fr[i];
}
if(L.tot1==1&&R.l0)res.fl[min(2,L.tot0+R.l0)]++,res.fl[2]+=R.l0-1;
if(R.tot1==1&&L.r0)res.fr[min(2,R.tot0+L.r0)]++,res.fr[2]+=L.r0-1;
res.sum=L.sum+R.sum;
res.sum+=
(ll)L.dr[0][0]*R.dl[0][1]+
(ll)L.dr[0][0]*R.dl[1][1]+
(ll)L.dr[0][1]*R.dl[0][0]+
(ll)L.dr[0][1]*R.dl[1][0]+
(ll)L.dr[1][0]*R.dl[0][1]+
(ll)L.dr[1][1]*R.dl[0][0];
if(R.l0)res.sum+=(ll)R.l0*(L.fr[0]+L.fr[1]+L.fr[2])-L.fr[0];
if(L.r0)res.sum+=(ll)L.r0*(R.fl[0]+R.fl[1]+R.fl[2])-R.fl[0];
res.l0=L.tot1==0?L.l0+R.l0:L.l0;
res.r0=R.tot1==0?R.r0+L.r0:R.r0;
res.tot0=L.tot0+R.tot0;
res.tot1=L.tot1+R.tot1;
return res;
}
};
cs int N=1e5+5;
node t[N<<2];
bool a[N];
inline void build(int k,cs int &l,cs int &r){
if(l==r){
t[k]=node(a[l]);
return ;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
t[k]=t[k<<1]+t[k<<1|1];
}
inline void modify(int k,cs int &l,cs int &r,cs int &pos){
if(l==r){
t[k]=node(a[l]^=1);
return ;
}
int mid=(l+r)>>1;
if(pos<=mid)modify(k<<1,l,mid,pos);
else modify(k<<1|1,mid+1,r,pos);
t[k]=t[k<<1]+t[k<<1|1];
}
inline node query(int k,cs int &l,cs int &r,cs int &ql,cs int &qr){
if(ql<=l&&r<=qr)return t[k];
int mid=(l+r)>>1;
if(qr<=mid)return query(k<<1,l,mid,ql,qr);
if(mid<ql)return query(k<<1|1,mid+1,r,ql,qr);
return query(k<<1,l,mid,ql,qr)+query(k<<1|1,mid+1,r,ql,qr);
}
int n,m;
signed main(){
n=getint();
for(int re i=1;i<=n;++i)a[i]=getint();
build(1,1,n);
m=getint();
while(m--){
switch(getint()){
case 1:modify(1,1,n,getint());break;
case 2:{
int l=getint(),r=getint();
cout<<(ll)(r-l+1)*(r-l+2)/2-query(1,1,n,l,r).sum<<"\n";
break;
}
}
}
return 0;
}