[TJOI2009] 开关
题目描述
现有 n n n 盏灯排成一排,从左到右依次编号为: 1 1 1, 2 2 2,……, n n n。然后依次执行 m m m 项操作。
操作分为两种:
- 指定一个区间 [ a , b ] [a,b] [a,b],然后改变编号在这个区间内的灯的状态(把开着的灯关上,关着的灯打开);
- 指定一个区间 [ a , b ] [a,b] [a,b],要求你输出这个区间内有多少盏灯是打开的。
灯在初始时都是关着的。
输入格式
第一行有两个整数 n n n 和 m m m,分别表示灯的数目和操作的数目。
接下来有 m m m 行,每行有三个整数,依次为: c c c、 a a a、 b b b。其中 c c c 表示操作的种类。
- 当 c c c 的值为 0 0 0 时,表示是第一种操作。
- 当 c c c 的值为 1 1 1 时,表示是第二种操作。
a a a 和 b b b 则分别表示了操作区间的左右边界。
输出格式
每当遇到第二种操作时,输出一行,包含一个整数,表示此时在查询的区间中打开的灯的数目。
样例 #1
样例输入 #1
4 5
0 1 2
0 2 4
1 2 3
0 2 4
1 1 4
样例输出 #1
1
2
提示
数据规模与约定
对于全部的测试点,保证 2 ≤ n ≤ 1 0 5 2\le n\le 10^5 2≤n≤105, 1 ≤ m ≤ 1 0 5 1\le m\le 10^5 1≤m≤105, 1 ≤ a , b ≤ n 1\le a,b\le n 1≤a,b≤n, c ∈ { 0 , 1 } c\in\{0,1\} c∈{0,1}。
思路分析
- 当时间复杂度 O ( n ) < 1 0 5 O(n)<10^5 O(n)<105的时候,单点修改/区间修改、区间查询的时候,就得想到线段树/树状数组,如果出现区间修改的话,我们就得用懒标记,每道题懒标记的形式和意义都不一样,就比如这道题懒标记就是记录灯亮与灭(懒标记最大的最大的作用就是pushdown的时候,降低重复计算的时间复杂度,为什么呢?懒标记是把某个区间标记了,如果该区间被修改,我们只需修改懒标记即可,不需要修改整个线段树,这样就节省了时间复杂度)
代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e5+10;
struct E{
int l,r;
int sum;
int add;
}tr[4*N];
int n,m;
void pushup(int u){
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
void pushdown(int u){
auto &root=tr[u],&left=tr[u<<1],&right=tr[u<<1|1];
if(root.add){
left.add^=1,left.sum=(left.r-left.l+1-left.sum);
right.add^=1,right.sum=(right.r-right.l+1-right.sum);
root.add=0;
}
}
//重点在于pushdown \text{pushdown}pushdown操作,注意到如果原来已经有了开关一次的标记,那么再次标记这个区间时就相当于消除原来这个标记,否则对这个节点进行标记。
void build(int u,int l,int r){
if(l==r)tr[u]={l,r,0,0};
else{
tr[u]={l,r};
pushdown(u);
int mid=(l+r)>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
}
void modify(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){
tr[u].sum=tr[u].r-tr[u].l+1-tr[u].sum;
tr[u].add^=1;
}else{
pushdown(u);
int mid=(tr[u].l+tr[u].r)>>1;
if(l<=mid)modify(u<<1,l,r);
if(r>mid)modify(u<<1|1,l,r);
pushup(u);
}
}
int query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r)return tr[u].sum;
pushdown(u);
int mid=(tr[u].l+tr[u].r)>>1;
int v=0;
if(l<=mid)v=query(u<<1,l,r);
if(r>mid)v+=query(u<<1|1,l,r);
return v;
}
int main(){
cin>>n>>m;
build(1,1,n);
while(m--){
int op,a,b;
cin>>op>>a>>b;
if(op==0){
modify(1,a,b);
}else{
cout<<query(1,a,b)<<endl;
}
}
return 0;
}