bzoj5294: [Bjoi2018]二进制(线段树)

传送门
题意简述:
给出一个长度为 n n n的二进制串。
你需要支持如下操作:

  1. 修改每个位置:1变0,0变1
  2. 询问对于一个区间的子二进制串有多少满足重排之后转回十进制值为 3 3 3的倍数(允许前导 0 0 0)。

思路:
考虑一个 x x x位的包含有 y y y 1 1 1的二进制串,它是 3 3 3的倍数当如下任意一个条件成立:

  1. y y y是偶数。
  2. y y y是大于 1 1 1的奇数且 x − y > = 2 x-y>=2 xy>=2

e m m m m emmmm emmmm感觉不是很好维护。
于是我们正难则反,跑去求不合法的方案数,这个二进制串不合法如下任意一个条件成立:

  1. y y y是奇数大于 1 1 1 x − y &lt; 2 x-y&lt;2 xy<2
  2. y = 1 y=1 y=1

这个答案可以用线段树维护了(其实上面的也可以只是感觉不太好写)。
我们定义两类状态:

  • C 0 / 1 , 0 / 1 , 0 / 1 , 0 / 1 C_{0/1,0/1,0/1,0/1} C0/1,0/1,0/1,0/1表示从区间左/右端点开始的连续一段二进制串,满足其中有偶/奇数个 1 1 1,串中 1 1 1的个数不大于/大于 1 1 1个,串中有 0 / 1 0/1 0/1 0 0 0的这样的二进制串个数。
  • D 0 / 1 , 0 / 1 D_{0/1,0/1} D0/1,0/1表示从区间左/右端点开始的连续一段二进制串,满足其中有 0 / 1 0/1 0/1 1 1 1的这样的二进制串的个数。

然后分类转移一下即可。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int N=1e5+5;
typedef long long ll;
int n;
bool a[N];
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
inline int add(const ll&a,const ll&b){return !a*b?0:a+b;}
namespace SGT{
	#define lc (p<<1)
	#define rc (p<<1|1)
	#define mid (T[p].l+T[p].r>>1)
	struct Node{int l,r,det;ll ans,c[2][2][2][2],d[2][2];}T[N<<2];
	inline Node operator+(const Node&a,const Node&b){
		Node ret;
		ret.l=a.l,ret.r=b.r,ret.det=a.det+b.det,ret.ans=a.ans+b.ans,memset(ret.c,0,sizeof(ret.c));
		ret.d[0][0]=a.d[0][0]+(a.det?0:b.d[0][0]);
		ret.d[0][1]=a.d[0][1]+(a.det<2?b.d[0][1-a.det]:0);
		ret.d[1][0]=b.d[1][0]+(b.det?0:a.d[1][0]);
		ret.d[1][1]=b.d[1][1]+(b.det<2?a.d[1][1-b.det]:0);
		for(ri i=0;i<2;++i)for(ri j=0;j<2;++j)for(ri k=0;k<2;++k)ret.c[0][i][j][k]=a.c[0][i][j][k],ret.c[1][i][j][k]=b.c[1][i][j][k];
		int tl=a.r-a.l+1-a.det,tr=b.r-b.l+1-b.det;
		for(ri k=0;k+tl<2;++k){
			ret.c[0][0][0][k+tl]+=a.det?0:b.c[0][0][0][k];
			ret.c[0][0][1][k+tl]+=b.c[0][a.det&1][1][k]+(a.det?b.c[0][a.det&1][0][k]:0);
			ret.c[0][1][0][k+tl]+=a.det<2?b.c[0][1-a.det][0][k]:0;
			ret.c[0][1][1][k+tl]+=b.c[0][(a.det&1)^1][1][k]+(a.det>1?b.c[0][(a.det&1)^1][0][k]:0);
		}
		for(ri k=0;k+tr<2;++k){
			ret.c[1][0][0][k+tr]+=b.det?0:a.c[1][0][0][k];
			ret.c[1][0][1][k+tr]+=a.c[1][b.det&1][1][k]+(b.det?a.c[1][b.det&1][0][k]:0);
			ret.c[1][1][0][k+tr]+=b.det<2?a.c[1][1-b.det][0][k]:0;
			ret.c[1][1][1][k+tr]+=a.c[1][(b.det&1)^1][1][k]+(b.det>1?a.c[1][(b.det&1)^1][0][k]:0);
		}
		for(ri i=0;i<2;++i)for(ri j=0;i+j<2;++j){
			ret.ans+=a.c[1][0][0][i]*b.c[0][1][1][j]+a.c[1][0][1][i]*b.c[0][1][1][j]+a.c[1][0][1][i]*b.c[0][1][0][j];
            ret.ans+=a.c[1][1][0][i]*b.c[0][0][1][j]+a.c[1][1][1][i]*b.c[0][0][1][j]+a.c[1][1][1][i]*b.c[0][0][0][j];
        }
		ret.ans+=a.d[1][0]*b.d[0][1]+a.d[1][1]*b.d[0][0];
		return ret;
	}
	inline void solve(int p){
		T[p].ans=T[p].det=0,memset(T[p].c,0,sizeof(T[p].c)),memset(T[p].d,0,sizeof(T[p].d));
		if(a[T[p].l])T[p].ans=T[p].det=T[p].c[0][1][0][0]=T[p].c[1][1][0][0]=T[p].d[0][1]=T[p].d[1][1]=1;
		else T[p].c[0][0][0][1]=T[p].c[1][0][0][1]=T[p].d[0][0]=T[p].d[1][0]=1;
	}
	inline void build(int p,int l,int r){
		T[p].l=l,T[p].r=r;
		if(l==r)return solve(p);
		build(lc,l,mid),build(rc,mid+1,r),T[p]=T[lc]+T[rc];
	}
	inline void update(int p,int k){
		if(T[p].l==T[p].r)return solve(p);
		update(k<=mid?lc:rc,k),T[p]=T[lc]+T[rc];
	}
	inline Node query(int p,int ql,int qr){
		if(ql<=T[p].l&&T[p].r<=qr)return T[p];
		if(qr<=mid)return query(lc,ql,qr);
		if(ql>mid)return query(rc,ql,qr);
		return query(lc,ql,mid)+query(rc,mid+1,qr);
	}
}
int main(){
	n=read();
	for(ri i=1;i<=n;++i)a[i]=read();
	SGT::build(1,1,n);
	for(ri tt=read(),op,l,r;tt;--tt){
		op=read();
		if(op==1)a[l=read()]^=1,SGT::update(1,l);
		else l=read(),r=read(),cout<<(ll)(r-l+1)*(r-l+2)/2-SGT::query(1,l,r).ans<<'\n';
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值