[BJOI2018] 二进制

一、题目

点此看题

二、解法

首先研究什么样的子串是可重排的,可以把二进制位理解成给 \(0\) 或者 \(1\) 一个权重。如果是奇数位置(从低往高)则权重为正一,如果是偶数位置则权重为负一,要求所有 \(1\) 的权重之和是 \(3\) 的倍数。

考虑如果 \(1\) 的个数为偶数,可以直接正一负一相抵消,一定存在解。如果 \(1\) 的个数为大于 \(1\) 的奇数,考虑构造三个同奇偶的位置也可以让权重之和是 \(3\) 的倍数,若总长度是偶数,则要求 \(0\) 的个数大于 \(1\);若总长度是奇数,则要求存在 \(0\)

可重排的情况太复杂了,考虑计算不可重排的情况。根据上面的分析,可以写出不可重排的充要条件是:

  • \(1\) 的个数是 \(1\)\(0\) 的个数 \(\geq 2\)
  • \(1\) 的个数为奇数且 \(0\) 的个数 \(<2\)

直接上线段树维护这东西,上面两部分可以分别计算:

  • 对于第一部分,维护 \(l_0,l_1,r_0,r_1\) 分别表示 \(1\) 的个数为 \(0/1\),这样的前缀\(/\)后缀有多少个。

  • 对于第二部分,维护 \(L[i][j]\)\(i,j\in[0,1]\))表示有多少个前缀 \(0\) 的个数为 \(i\)\(1\) 的奇偶性为 \(j\)\(R[i][j]\) 表示这样的后缀有多少个。

上述的信息都是易于合并的,拿他们计算答案也很容易。一个小细节是合并时中间 \(10/01\) 的情况可能会多算一次,注意去重。时间复杂度 \(O(n\log n)\)

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 100005;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,mid,z[M];
struct node
{
	int l0,l1,r0,r1,c0,c1;
	int l[2][2],r[2][2],res;
	node()
	{
		l0=l1=r0=r1=c0=c1=res=0;
		memset(l,0,sizeof l);memset(r,0,sizeof r);
	}
	void init(int x)
	{
		(*this)=node();
		if(x) l1=r1=c1=l[0][1]=r[0][1]=res=1;
		else l0=r0=c0=l[1][0]=r[1][0]=1;
	}
	
}t[M<<2];
node merge(node a,node b,int mid)
{
	node x;
	x.c0=a.c0+b.c0;x.c1=a.c1+b.c1;
	x.l0=a.l0+(!a.c1?b.l0:0);
	x.r0=b.r0+(!b.c1?a.r0:0);
	x.l1=a.l1+(!a.c1?b.l1:0)+(a.c1==1?b.l0:0);
	x.r1=b.r1+(!b.c1?a.r1:0)+(b.c1==1?a.r0:0);
	//
	for(int i=0;i<2;i++) for(int j=0;j<2;j++)
	{
		x.l[i][j]=a.l[i][j]+(i>=a.c0?b.l[i-a.c0][j^(a.c1&1)]:0);
		x.r[i][j]=b.r[i][j]+(i>=b.c0?a.r[i-b.c0][j^(b.c1&1)]:0);
	}
	//
	x.res=a.res+b.res;
	x.res+=a.r0*b.l1+a.r1*b.l0;
	x.res+=a.r[0][0]*(b.l[0][1]+b.l[1][1]);
	x.res+=a.r[0][1]*(b.l[0][0]+b.l[1][0]);
	x.res+=a.r[1][0]*b.l[0][1]+a.r[1][1]*b.l[0][0];
	//
	if(z[mid]+z[mid+1]==1) x.res--;
	return x;
}
void build(int i,int l,int r)
{
	if(l==r) {t[i].init(z[l]);return ;}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	t[i]=merge(t[i<<1],t[i<<1|1],mid);
}
void upd(int i,int l,int r,int x)
{
	if(l==r) {t[i].init(z[l]);return ;}
	int mid=(l+r)>>1;
	if(mid>=x) upd(i<<1,l,mid,x);
	else upd(i<<1|1,mid+1,r,x);
	t[i]=merge(t[i<<1],t[i<<1|1],mid);
}
node ask(int i,int l,int r,int L,int R)
{
	if(L<=l && r<=R) return t[i];
	int mid=(l+r)>>1;
	if(R<=mid) return ask(i<<1,l,mid,L,R);
	if(mid<L) return ask(i<<1|1,mid+1,r,L,R);
	return merge(ask(i<<1,l,mid,L,R),
	ask(i<<1|1,mid+1,r,L,R),mid);
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++) z[i]=read();
	build(1,1,n);
	m=read();
	for(int i=1;i<=m;i++)
	{
		int op=read(),l=read();
		if(op==1) z[l]^=1,upd(1,1,n,l);
		if(op==2)
		{
			int r=read(),sum=(r-l+1)*(r-l+2)/2;
			printf("%lld\n",sum-ask(1,1,n,l,r).res);
		}
	}
}
posted @ 2022-07-27 21:26  C202044zxy  阅读(138)  评论(0编辑  收藏  举报