洛谷 P3870 [TJOI2009]開關 / P2846 [USACO08NOV]光開關Light Switching
阿新 • • 發佈:2018-12-08
水題,不知道哪來的省選難度
線段樹維護一下就行
這裡我用了個小技巧:
開的燈關了,關的燈開了,燈的總數不變
只是把這兩者數量交換下
例如: 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; }