1. 程式人生 > >洛谷 P3870 [TJOI2009]開關 / P2846 [USACO08NOV]光開關Light Switching

洛谷 P3870 [TJOI2009]開關 / P2846 [USACO08NOV]光開關Light Switching

題目

水題,不知道哪來的省選難度   
線段樹維護一下就行  

這裡我用了個小技巧:   
開的燈關了,關的燈開了,燈的總數不變    
只是把這兩者數量交換下   

例如:    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;
}