携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第27天,点击查看活动详情
[TJOI2009] 开关
题目描述
现有 盏灯排成一排,从左到右依次编号为:,,……,。然后依次执行 项操作。
操作分为两种:
- 指定一个区间 ,然后改变编号在这个区间内的灯的状态(把开着的灯关上,关着的灯打开);
- 指定一个区间 ,要求你输出这个区间内有多少盏灯是打开的。
灯在初始时都是关着的。
输入格式
第一行有两个整数 和 ,分别表示灯的数目和操作的数目。
接下来有 行,每行有三个整数,依次为:、、。其中 表示操作的种类。
- 当 的值为 时,表示是第一种操作。
- 当 的值为 时,表示是第二种操作。
和 则分别表示了操作区间的左右边界。
输出格式
每当遇到第二种操作时,输出一行,包含一个整数,表示此时在查询的区间中打开的灯的数目。
样例 #1
样例输入 #1
4 5
0 1 2
0 2 4
1 2 3
0 2 4
1 1 4
样例输出 #1
1
2
提示
数据规模与约定
对于全部的测试点,保证 ,,,。
#include <bits/stdc++.h>
#define SIZE 1000030
using namespace std;
struct SegmentTree {
int l, r;
int l0, l1, add;
// l0 表示 关着的灯的数量,
// l1 表示 亮着的灯的数量
} t[SIZE * 4];
int n, m, opt, x, y;
void build(int p, int l, int r) {//建树
t[p].l = l; t[p].r = r;
if (l == r) {
t[p].l0++;
return ;
}
int mid = (l + r) / 2;
build(p<<1, l, mid);
build(p<<1|1, mid + 1, r);
t[p].l0 = t[p<<1].l0 + t[p<<1|1].l0;
}
inline void spread(int p) {//打标记
if (t[p].add == 0) return ;
swap(t[p<<1].l0, t[p<<1].l1); //交换
swap(t[p<<1|1].l0, t[p<<1|1].l1);
t[p<<1].add ^= 1; //开关灯就相当于 ^ 一下
t[p<<1|1].add ^= 1;
t[p].add = 0;
}
inline void change(int p, int l, int r) {
//开关灯,线段树板子
if (t[p].l >= l && t[p].r <= r) {
t[p].add ^= 1;
swap(t[p].l0, t[p].l1);
return ;
}
spread(p);
int mid = (t[p].l + t[p].r) / 2;
if (l <= mid) change(p<<1, l, r);
if (mid < r) change(p<<1|1, l, r);
t[p].l0 = t[p<<1].l0 + t[p<<1|1].l0;
t[p].l1 = t[p<<1].l1 + t[p<<1|1].l1;
}
int ask(int p, int l, int r) {
//查询开着的灯的数量
if (t[p].l >= l && t[p].r <= r)
return t[p].l1;
spread(p);
int mid = (t[p].l + t[p].r) / 2, sum = 0;
if (l <= mid) sum += ask(p<<1, l, r);
if (mid < r) sum += ask(p<<1|1, l, r);
return sum;
}
inline void solve() {
scanf ("%d%d", &n, &m);
build(1, 1, n);
for (int i = 1; i <= m; ++i) {
scanf ("%d%d%d", &opt, &x, &y);
if (opt == 0)
change(1, x, y);
else
printf("%d\n", ask(1, x, y));
}
}
int main() {
solve();
return 0;
}