版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40101479/article/details/82949826
水题,不知道哪来的省选难度
线段树维护一下就行
这里我用了个小技巧:
开的灯关了,关的灯开了,灯的总数不变
只是把这两者数量交换下
例如: 1 0 1 0 1 1
开着的灯的数量:4
关着的灯的数量:2
操作一次后: 0 1 0 1 0 0
开着的灯的数量:2
关着的灯的数量:4
就是把2和4交换下,似乎比较容易理解
代码:
#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;
inline 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;
}