• [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);
    		}
    	}
    }
    
  • 相关阅读:
    NuGet打包推送批处理以及MSBuild(通用版)
    Linux su和sudo命令的区别,并获得root权限
    linux下命令运行目录上程序前面要加./
    java的系统时间,怎么计算从现在到凌晨还剩下多少时间?
    静态资源压缩(GZIP) 专题
    架构选型之Nodejs与Java
    基于 WebRTC 创建一款多人联机游戏
    django从0到1搭建网站
    Android 关于ExpandableListView去掉里头的分割线
    Android 关于ExpandableListView刷新的解决办法
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16526557.html
Copyright © 2020-2023  润新知