P3870 [TJOI2009] 开关 题解

原题传送门

思路

一眼线段树板子题。

用线段树来维护每一个区间和更改区间的值。

tag 函数主要是维护儿子的 sum 值。

懒标记在此题中主要指这个区间是否需要更改。

AC代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1145141;
int ans,n,m,a,b,c;
struct tree {
	int l,r,sum,add;
} t[maxn];
void build(int u,int x,int y) {
	t[u].l=x;
	t[u].r=y;
	if(x==y) {
		t[u].sum=0;
		return ;
	}
	int mid=(x+y)>>1;
	build(u*2,x,mid);
	build(u*2+1,mid+1,y);
}//建树 
void tag(int u) {
	if(t[u].add==0)
		return;//如果父亲不动,那儿子也不动 
	t[u*2].sum=t[u*2].r-t[u*2].l+1-t[u*2].sum;
	t[u*2+1].sum=t[u*2+1].r-t[u*2+1].l+1-t[u*2+1].sum;//维护儿子的sum值
	if(t[u*2].add==0){
		t[u*2].add=1;
	}//如果左儿子没有懒标记的话就给他懒标记 
	else{
		t[u*2].add=0;
	}
	if(t[u*2+1].add==0){
		t[u*2+1].add=1;
	}
	else{
		t[u*2+1].add=0;
	}//处理右儿子,跟左儿子一样 
	t[u].add=0;//左右儿子的父亲已经修改过了,所以父亲的懒标记要归0,也就是说父亲不需要改。
}
void change(int u,int l,int r) {
	if(l<=t[u].l&&t[u].r<=r) {
		t[u].sum=t[u].r-t[u].l+1-t[u].sum;
		if(t[u].add==0){
			t[u].add=1;
		}
		else{
			t[u].add=0;
		}
		return ;
	}
	tag(u);
	int mid=(t[u].l+t[u].r)>>1;
	if(a<=mid){
		change(u*2,l,r);
	}
	if(b>mid){
		change(u*2+1,l,r);	
	}
	t[u].sum=t[u*2].sum+t[u*2+1].sum;
}//改状态 
int ask(int u,int l,int r) {
	if(l<=t[u].l&&r>=t[u].r){
		return t[u].sum;
	}
	tag(u);
	int mid=(t[u].l+t[u].r)/2;
	int ans=0;
	if(a<=mid){
		ans+=ask(u*2,l,r);
	}
	if(b>mid){
		ans+=ask(u*2+1,l,r);
	}//二分查 
	return ans;
}//查询 
int main() {
	cin>>n>>m;
	build(1,1,n);
	for(int i=1; i<=m; i++) {
		cin>>c>>a>>b;
		if(c==0){
			change(1,a,b);
		}
		else{
			cout<<ask(1,a,b)<<endl;
		}
	}
	return 0;
}
posted @ 2023-12-04 18:44  IOI_official  阅读(46)  评论(0)    收藏  举报  来源