P3870 [TJOI2009]开关 题解

Link

P3870 [TJOI2009]开关

Solve

把序列分成\(\sqrt{N}\)个块,每个块整体处理时亮着的灯数=块内灯总数-前一时刻亮着的灯数,块外的单独处理就好了。

貌似线段树也是这种统计方式

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
int N,Q,a[maxn],L[maxn],pos[maxn],R[maxn],add[maxn],sum[maxn],t,light[maxn];
inline int read(){
	int ret=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
	while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
	return ret*f;
}
void change(int l,int r){
	int p=pos[l],q=pos[r];
	if(p==q){
		for(int i=l;i<=r;i++){
			a[i]^=1;
			if(a[i]^add[p])light[p]++;
			else light[p]--;
		}
	}
	else {
		for(int i=p+1;i<=q-1;i++){add[i]^=1;light[i]=sum[i]-light[i];}
		for(int i=l;i<=R[p];i++){
			a[i]^=1;
			if(a[i]^add[p])light[p]++;
			else light[p]--;
		}
		for(int i=L[q];i<=r;i++){
			a[i]^=1;
			if(a[i]^add[q])light[q]++;
			else light[q]--;
		}
	}
	return ;
}
int query(int l,int r){
	int ans=0,p=pos[l],q=pos[r];
	if(p==q){
		for(int i=l;i<=r;i++)ans+=a[i]^add[p];
	}
	else{
		for(int i=p+1;i<=q-1;i++)ans+=light[i];
		for(int i=l;i<=R[p];i++)ans+=a[i]^add[p];
		for(int i=L[q];i<=r;i++)ans+=a[i]^add[q];
	}
	return ans;
}
int main(){
	freopen("P3870.in","r",stdin);
	freopen("P3870.out","w",stdout);
	N=read();Q=read();
	t=sqrt(N);
	for(int i=1;i<=t;i++){
		L[i]=(i-1)*t+1;
		R[i]=i*t;
	}
	if(R[t]<N){t++;L[t]=R[t-1]+1;R[t]=N;}
	for(int i=1;i<=t;i++)
	for(int j=L[i];j<=R[i];j++){
		pos[j]=i;
		sum[i]++;
	}
	while(Q--){
		int op=read(),l=read(),r=read();
		if(!op)change(l,r);
		else printf("%d\n",query(l,r));
	}
	return 0;
}
上一篇:[luogu2617][bzoj1901][Zju2112]Dynamic Rankings【树套树+树状数组+主席树】


下一篇:PAT甲级专题|链表