线段树经典入门题目;
这道题有几点不同的地方:
1. 维护的是集合,一个点或者一段区间都是一个集合,(2,3) 是一个集合,不能简单的用线段代替集合;
2. 用离散的 01 值表示集合,对集合的运算需要转化为 01 区间的异或与赋值操作;
3. 01 区间上进行异或和赋值,在 pushdown 的时候只能存在一个信息,一个线段要么被赋值,要么异或,不能既被赋值,同时进行异或(cover[]和xor[]不能同时有效);
4. 结果要求输出运算结果(集合),在查询时,要做的不是查询到左端点、右端点,而是把被赋值的区间全部标记下来(O(n)),然后一次扫描完成输出,这道题卡了很长时间就是这里没想清楚(既然用了线段树,为什么还要O(n)呢?其实是只需要一次O(n),何必用线段树呢?);
# include <stdio.h> # include <string.h> # define ls ((r)<<1) # define rs ((r)<<1 | 1) # define mid (((x)+(y))>>1) # define maxn (65535 * 2) char cc[maxn << 2], xx[maxn << 2], hh[maxn+1]; /*************************************************************/ void XOR(int r) { if (cc[r] != -1) cc[r] ^= 1; else xx[r] ^= 1; } void pushdown(int x, int y, int r) { /* after pushdown cc[r]==-1 && xx[r]==0 */ if (cc[r] != -1) { cc[ls] = cc[rs] = cc[r]; xx[ls] = xx[rs] = 0; cc[r] = -1; } if (xx[r]) { XOR(ls); XOR(rs); xx[r] = 0; } } void cover(int x, int y, int r, int s, int t, int val) { if (s<=x && y<=t) { cc[r] = val; xx[r] = 0; return ; } pushdown(x, y, r); if (s <= mid) cover(x, mid, ls, s, t, val); if (mid+1<=t) cover(mid+1, y, rs, s, t, val); } void xxor(int x, int y, int r, int s, int t) { if (s<=x && y<=t) { XOR(r); return ; } pushdown(x, y, r); if (s <= mid) xxor(x, mid, ls, s, t); if (mid+1<=t) xxor(mid+1, y, rs, s, t); } void query(int x, int y, int r) { int i; if (cc[r]!= -1) { if (cc[r] == 1) for (i = x; i <= y; ++i) hh[i] = 1; return ; } // if (x == y) return ; pushdown(x, y, r); query(x, mid, ls); query(mid+1, y, rs); } /*************************************************************/ int main() { freopen("data.in", "r", stdin); freopen("data.out", "w", stdout); int s, t, i, first = 1; char op, lp, rp; while (~scanf("%c %c%d,%d%c\n", &op, &lp, &s, &t, &rp)) { s <<= 1, t <<= 1; if (lp == '(') ++s; if (rp == ')') --t; if (s > t || (s==t && (s&0x1))) { if (op == 'I' || op == 'C') cc[1] = 0, xx[1] = 0; continue; } switch(op) { case 'U': cover(0, maxn, 1, s, t, 1);break; case 'D': cover(0, maxn, 1, s, t, 0);break; case 'I': { if (s>0) cover(0, maxn, 1, 0, s-1, 0); if (t<maxn) cover(0, maxn, 1, t+1, maxn, 0); break; } case 'C': { if (s>0) cover(0, maxn, 1, 0, s-1, 0); if (t<maxn) cover(0, maxn, 1, t+1, maxn, 0); xxor(0, maxn, 1, s, t); break; } case 'S': xxor(0, maxn, 1, s, t);break; } } query(0, maxn, 1); for (i = 0; i <= maxn; ++i) { if (hh[i]) { if (first) first = 0; else putchar(' '); printf("%c%d,", i&0x1 ? '(':'[', i>>1); while (hh[i]) ++i; printf("%d%c", i>>1, (i-1)&0x1 ? ')':']'); } } if (first) puts("empty set"); else putchar('\n'); return 0; }
代码写的丑,但是刚好跑到了notonlysuccess的前面,谢谢他的线段树专辑。