纯属抄袭胡浩大神的题解。。。看了N久才看懂o(╯□╰)o,果然智商拙计,学习到了一种处理开区间的方法(感觉好神奇),当个搬运工。。。
题目大意:
区间操作,交,并,补等
题解:
我们一个一个操作来分析:(用0和1表示是否包含区间,-1表示该区间内既有包含又有不包含)
U:把区间[l,r]覆盖成1
I:把[-∞,l)(r,∞]覆盖成0
D:把区间[l,r]覆盖成0
C:把[-∞,l)(r,∞]覆盖成0 , 且[l,r]区间0/1互换
S:[l,r]区间0/1互换
成段覆盖的操作很简单,比较特殊的就是区间0/1互换这个操作,我们可以称之为异或操作
很明显我们可以知道这个性质:当一个区间被覆盖后,不管之前有没有异或标记都没有意义了
所以当一个节点得到覆盖标记时把异或标记清空
而当一个节点得到异或标记的时候,先判断覆盖标记,如果是0或1,直接改变一下覆盖标记,不然的话改变异或标记
开区间闭区间只要数字乘以2就可以处理(偶数表示端点,奇数表示两端点间的区间)
线段树功能:update:成段替换,区间异或 query:简单hash
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define lson l , m , s << 1 #define rson m+1 , r , s << 1 | 1 #define MAXN 150000 int setv[MAXN<<2],Xor[MAXN<<2]; bool hash[MAXN]; void FXor(int s) { if(setv[s]!=-1) setv[s]^=1; else Xor[s]^=1; } void PushDown(int s) { if(setv[s]!=-1) { setv[s<<1]=setv[s<<1|1]=setv[s]; Xor[s<<1]=Xor[s<<1|1]=0; setv[s]=-1; } if(Xor[s]==1) { FXor(s<<1); FXor(s<<1|1); Xor[s]=0; } } void update(char op,int ql,int qr,int l,int r,int s) { if(ql<=l&&r<=qr) { if(op=='U') { setv[s]=1; Xor[s]=0; } else if(op=='D') { setv[s]=0; Xor[s]=0; } else if(op=='C'||op=='S') FXor(s); return; } PushDown(s); int m=(l+r)>>1; if(ql<=m) update(op,ql,qr,lson); else if(op=='I'||op=='C') Xor[s<<1]=setv[s<<1]=0; if(qr>m) update(op,ql,qr,rson); else if(op=='I'||op=='C') Xor[s<<1|1]=setv[s<<1|1]=0; } void query(int l,int r,int s) { if(setv[s]==1) { for(int i=l; i<=r; i++) hash[i]=true; return; } else if(setv[s]==0) return; if(l==r) return; PushDown(s); int m=(l+r)>>1; query(lson); query(rson); } int main(void) { char op,a,b; int l,r; setv[1]=Xor[1]=0; memset(hash,false,sizeof(hash)); while(scanf("%c %c%d,%d%c\n",&op,&a,&l,&r,&b)!=EOF) { l<<=1; r<<=1; if(a=='(') l++; if(b==')') r--; update(op,l,r,0,MAXN,1); } query(0,MAXN,1); int s=-1,t; bool flag=false; for(int i=0; i<MAXN; i++) if(hash[i]) { if(s==-1) s=i; t=i; } else if(s!=-1) { if(flag) printf(" "); flag=true; printf("%c%d,%d%c",s&1?'(':'[',s>>1,(t+1)>>1,t&1?')':']'); s=-1; } if(!flag) printf("empty set"); printf("\n"); return 0; }