【题目】
题目描述:
现有 ( ≤ ≤ )盏灯排成一排,从左到右依次编号为: , ,…, 。然后依次执行 ( ≤ ≤ )项操作,操作分为两种:第一种操作指定一个区间 [ ] ,然后改变编号在这个区间内的灯的状态(把开着的灯关上,关着的灯打开),第二种操作是指定一个区间 [ ] ,要求你输出这个区间内有多少盏灯是打开的。灯在初始时都是关着的。
输入格式:
第一行有两个整数
和
,分别表示灯的数目和操作的数目。
接下来有
行,每行有三个整数,依次为:
。其中
表示操作的种类,当
的值为
时,表示是第一种操作。当
的值为
时表示是第二种操作。
和
则分别表示了操作区间的左右边界(
≤
≤
≤
)。
输出格式:
每当遇到第二种操作时,输出一行,包含一个整数:此时在查询的区间中打开的灯的数目。
样例数据:
输入
4 5
0 1 2
0 2 4
1 2 3
0 2 4
1 1 4
输出
1
2
【分析】
题解:线段树
假设 表示关着的灯, 表示开着的灯
我们用两个数组 表示 中 的个数, 表示 中 的个数
然后只用累计取反操作的次数(累加或异或都可以),若要取反,则交换 和 即可
剩下的就是基础的线段树操作了
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200005
using namespace std;
int sum[N<<2][2],mark[N<<2];
void build(int root,int l,int r)
{
if(l==r)
{
sum[root][0]=1;
return;
}
int mid=(l+r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
sum[root][0]=sum[root<<1][0]+sum[root<<1|1][0];
}
void pushdown(int root)
{
swap(sum[root<<1][0],sum[root<<1][1]);
swap(sum[root<<1|1][0],sum[root<<1|1][1]);
mark[root<<1]^=1,mark[root<<1|1]^=1,mark[root]=0;
}
void modify(int root,int l,int r,int x,int y)
{
if(l>=x&&r<=y)
{
mark[root]^=1;
swap(sum[root][0],sum[root][1]);
return;
}
int mid=(l+r)>>1;
if(mark[root]) pushdown(root);
if(x<=mid) modify(root<<1,l,mid,x,y);
if(y>mid) modify(root<<1|1,mid+1,r,x,y);
sum[root][0]=sum[root<<1][0]+sum[root<<1|1][0];
sum[root][1]=sum[root<<1][1]+sum[root<<1|1][1];
}
int query(int root,int l,int r,int x,int y)
{
if(l>=x&&r<=y)
return sum[root][1];
int ans=0,mid=(l+r)>>1;
if(mark[root]) pushdown(root);
if(x<=mid) ans+=query(root<<1,l,mid,x,y);
if(y>mid) ans+=query(root<<1|1,mid+1,r,x,y);
return ans;
}
int main()
{
int n,m,i,s,x,y;
scanf("%d%d",&n,&m);
build(1,1,n);
for(i=1;i<=m;++i)
{
scanf("%d%d%d",&s,&x,&y);
if(s==0) modify(1,1,n,x,y);
if(s==1) printf("%d\n",query(1,1,n,x,y));
}
return 0;
}