NOI模拟 黑白划分【线段树】【容斥】

传送门

题目本质是求非同色正方形且边长为2的幂的个数。

这个2的幂条件简直给线段树量身定做。

Ans=\sum_{i=0}^n2^{2n-2i}-(4*cnt(i)[i>0])

cnt(i)即边长为2^i的同色正方形个数。纯色总数减去同色个数*4(因为有4的贡献被去掉了)就是异色贡献。

最多一百万行列,我们分边长来考虑。

对于边长为2^i的正方形,很明显其由一条长为2^i的横边和一条长为2^i的纵边围成,任何两条这样的边可以成为一个这样的正方形。

所以我们分横纵分别维护。

我们令黑,白权值分别为0,1

对于一个区间,我们维护这个区间颜色和。很明显,只有当和为0或者r-l+1时,才意味着这个区间颜色相同。

那每次分黑白讨论pushup即可。巧妙预处理lg很方便计算。

#include<bits/stdc++.h>
using namespace std;
#define in read()
#define mid (l+r>>1)
#define int long long
int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();if(ch=='-')f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;
		ch=getchar();
	}return cnt*f;
}
int lg[1400003];
struct node{
	int cnt[23];
	struct bili{
		int l,r,sum;
	}t[4000003];
	void pushup(int u,int l,int r){
		int pre=t[u].sum;
		t[u].sum=t[u*2].sum+t[u*2+1].sum;
		if(pre==1&&t[u].sum==0||pre==r-l&&t[u].sum==r-l+1)cnt[lg[r-l+1]]++;
		if(pre==0&&t[u].sum==1||pre==r-l+1&&t[u].sum==r-l)cnt[lg[r-l+1]]--;
	}
	void build(int u,int l,int r){
		t[u].l=l;t[u].r=r;//t[u].sum++;
		cnt[lg[r-l+1]]++;
		if(l==r)return;build(u*2,l,mid);build(u*2+1,mid+1,r);
		pushup(u,l,r);
	}
	void modify(int u,int l,int r,int pos){
		if(t[u].l>pos||t[u].r<pos)return;
		if(pos==t[u].l&&t[u].l==t[u].r){
			t[u].sum^=1;return;
		}modify(u*2,l,mid,pos);modify(u*2+1,mid+1,r,pos);
		pushup(u,l,r);
	}
}t[2];int n,q,s;
int ksm(int a,int b){
	int sum=1;
	while(b){
		if(b&1)sum=sum*a;a=a*a;b>>=1;
	}return sum;
}
signed main(){
for(int i=2;i<=1400000;i++)lg[i]=lg[i>>1]+1;
	n=in;q=in;s=(1<<n);t[0].build(1,1,s);t[1].build(1,1,s);
	while(q--){
		int op=in;int x=in;
		t[op].modify(1,1,s,x);
		int ans=0;
		for(int i=0;i<=n;i++){
			ans=(ans+ksm(2,2*n-2*i));
			if(i)ans=(ans-4*t[0].cnt[i]*t[1].cnt[i]);
		}cout<<ans<<'\n';
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值