题意:给你一些区间操作,让你输出最后得出的区间。
解法:区间操作的经典题,借鉴了网上的倍增算法,每次将区间乘以2,然后根据区间开闭情况做微调,这样可以有效处理开闭区间问题。
线段树维护两个值: cov 和 rev ,一个是覆盖标记,0表示此区间被0覆盖,1表示被1覆盖,-1表示未被覆盖, rev为反转标记,rev = 1表示反转,0表示不翻转
所以集合操作可以化为如下区间操作:
U l r: 把区间[l,r]覆盖成1
I l r: 把[0,l)(r,MAX]覆盖成0
D l r: 把区间[l,r]覆盖成0
C l r: 把[0,l)(r,MAX]覆盖成0 , 且[l,r]区间0/1互换
S l r: [l,r]区间0/1互换
重点在于pushdown函数以及边界处理。
代码:
#include <iostream> #include <cmath> #include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> using namespace std; #define N (65536*2) struct Tree { int cov,rev; //cov -1 rev 0 }tree[4*N]; struct ANS { char L,R; int A,B; }ans[N+1]; int cnt; void build(int l,int r,int rt) { tree[rt].cov = 0; tree[rt].rev = 0; if(l == r) return; int mid = (l+r)/2; build(l,mid,2*rt); build(mid+1,r,2*rt+1); } void pushdown(int l,int r,int rt) { if(tree[rt].cov != -1) { tree[2*rt].cov = tree[2*rt+1].cov = tree[rt].cov; tree[2*rt].rev = tree[2*rt+1].rev = 0; tree[rt].cov = -1; } if(tree[rt].rev) { if(tree[2*rt].cov != -1) tree[2*rt].cov ^= 1; else tree[2*rt].rev ^= 1; if(tree[2*rt+1].cov != -1) tree[2*rt+1].cov ^= 1; else tree[2*rt+1].rev ^= 1; tree[rt].rev = 0; } } void update(int l,int r,int aa,int bb,int op,int rt) { if(aa > bb || aa < 0) return; //必须要加,否则会RE if(aa <= l && bb >= r) { if(op != 2) //cover to 0/1 { tree[rt].cov = op; tree[rt].rev = 0; } else //op == 2 reverse { if(tree[rt].cov != -1) tree[rt].cov ^= 1; else tree[rt].rev ^= 1; } return; } pushdown(l,r,rt); int mid = (l+r)/2; if(aa <= mid) update(l,mid,aa,bb,op,2*rt); if(bb > mid) update(mid+1,r,aa,bb,op,2*rt+1); } void query(int l,int r,int rt) { if(tree[rt].cov == 1) { ans[cnt].L = (l%2==1)?'(':'['; ans[cnt].A = l/2; ans[cnt].R = (r%2==1)?')':']'; ans[cnt].B = (r+1)/2; cnt++; } else if(tree[rt].cov == 0) return; else { pushdown(l,r,rt); int mid = (l+r)/2; query(l,mid,2*rt); query(mid+1,r,2*rt+1); } } void print() { char nowl,nowr; int nowA,nowB; if(cnt == 0) puts("empty set"); else { nowl = ans[0].L; nowr = ans[0].R; nowA = ans[0].A; nowB = ans[0].B; for(int i=1;i<cnt;i++) { if(ans[i].A == nowB && (nowr == ']' || ans[i].L == '[')) { nowB = ans[i].B; nowr = ans[i].R; } else { printf("%c%d,%d%c ",nowl,nowA,nowB,nowr); nowl = ans[i].L; nowr = ans[i].R; nowA = ans[i].A; nowB = ans[i].B; } } printf("%c%d,%d%c\n",nowl,nowA,nowB,nowr); } } int main() { int a,b; char L,R,op; int n = 65536*2; build(0,n,1); while(scanf("%c %c%d,%d%c\n",&op,&L,&a,&b,&R)!=EOF) // '\n' 务必要加 { a = 2*a; if(L == '(') a++; b = 2*b; if(R == ')') b--; if(a > b || a < 0) continue; if(op == 'U') //并集 update(0,n,a,b,1,1); else if(op == 'I') { update(0,n,0,a-1,0,1); update(0,n,b+1,n,0,1); } else if(op == 'D') update(0,n,a,b,0,1); else if(op == 'C') { update(0,n,0,a-1,0,1); update(0,n,b+1,n,0,1); update(0,n,a,b,2,1); } else update(0,n,a,b,2,1); } cnt = 0; query(0,n,1); print(); return 0; }