2019.03.06【BJOI2018】【洛谷P4428】【BZOJ5293】二进制(线段树)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/88251203

BZOJ传送门

洛谷传送门


解析:

我太菜了,orz了yyb的题解才会这道题。

仿照十进制下 11 11 整除的数的特点,我们可以知道二进制下被 3 = 1 1 2 3=11_2 整除的数的特点。

由于二进制下每一位被三整除的余数是 1 , 2 , 1 , 2 , 1 , 2 , 1 1,2,1,2,1,2,1…… 排列的,其实也就是 1 , 1 , 1 , 1 1,-1,1,-1…… 的排列。

所以规律还是这个,二进制下奇偶数码和之差能够被 3 3 整除的,原数能够被 3 3 整除。

那么现在考虑什么样的区间重排之后能够被 3 3 整除。

  1. 偶数个 1 1 ,这种区间只需要把所有 0 0 全部扔到开头,就能够直接使剩下的 1 1 在奇数位置和偶数位置分别占一半,差为 0 0
  2. 1 1 的个数是不少于 3 3 个的奇数,且 0 0 不少于 2 2 个。
    其实这个还是挺好想的,如果只有一个 1 1 是肯定不行的。如果有不少于 3 3 1 1 ,我们需要摆出 10101 10101 ,然后剩下的偶数个 1 1 摆成连续的就行了。

感觉直接统计合法区间不是什么方便的事,反着统计不合法的区间:

  1. 一个 1 1 ,且 0 0 的个数大于等于 2 2 个。
  2. 奇数个 1 1 ,且 0 0 的个数少于 2 2 个。

注意这里为了防止重复统计,在第一条里面增加了限制。

现在考虑合并两个区间的同时,求出横跨了区间分界点的非法区间。

d l [ 0 / 1 ] [ 0 / 1 ] dl[0/1][0/1] 表示强制选择当前区间左端点的所有序列中, 0 0 出现了 0 / 1 0/1 次,且 1 1 出现次数的奇偶性 0 / 1 0/1 时的序列数。 d r dr 同理

f l [ 0 / 1 / 2 ] fl[0/1/2] 表示强制经过当前区间左端点的所有序列中, 1 1 出现了恰好 1 1 次, 0 0 出现了 0 / 1 / 2 0/1/\geq2 的序列数。 f r fr 同理

l 0 l_0 表示从当前区间的左端点开始的连续的 0 0 的个数。 r 0 r_0 同理

t o t 0 tot_0 表示当前区间的总的 0 0 的个数, t o t 1 tot1 同理。

s u m sum 就是当前区间所有非法子序列的总数。

合并成新的区间:

t o t 0 , t o t 1 tot_0,tot_1 的转移都十分显然。

l 0 , r 0 l_0,r_0 的转移判断一下左/右区间是否为全零就能轻易解决了

f l , f r fl,fr 需要先从当前合并的左右区间继承一下,然后根据左右区间 1 1 的个数是 0 / 1 0/1 的进行讨论。为 0 0 的时候直接跨区间加一下就行了。如果左/右区间刚好有一个 1 1 ,那么需要讨论跨区间的转移,但是如果另一个区间分界线处没有连续的 0 0 就只能咕咕咕了。

d l , d r dl,dr 的转移也是同理,先从左右区间继承,发现 0 0 不够的时候再考虑跨区间的转移。

s u m sum 的处理其实就是写起来吓人想起来sb。将左区间的 d l dl 和右区间的 d r dr 拼成新的转移,只需要保证新的区间 1 1 的个数为奇数且没有达到两个 0 0 就处理完情况 2 2 了。

然后考虑跨中间的情况 1 1 ,显然需要两边在区间分界线上有连续的 0 0 的时候才能转移,所有的 0 0 都能够和另一边的 f f 拼成新的区间,但是要考虑拼出来的 0 0 不超过 2 2 的情况,需要丢掉。


代码:

#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;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/88251203