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互换
因为普通的线段树实际处理的并非真正的区间,而是一系列点,相当于处理一个向量。这个问题需要处理的是真正的区间,所以应该有一个主导思想就是,把区间点化!不知哪位大牛搞了一个倍增区间出来,实在佩服!对于待处理区间[a,b](暂时不考虑开闭),对其边界均乘2。若区间左开则对左界值+1,若区间右开,则对右界-1!
如:[2,3]会倍增为[4,6],[2,3)会倍增为[4,5],(2,3]会倍增为[5,6],(2,3)将倍增为[5,5],我们这时可以看到,对于普通线段树无法处理的线段如(x,x+1)将被点化为[2*x+1,2*x+1]!这个问题得到比较完美的解决
最后把查找出来的区间逆向倍增操作一下,就可以得到实际的区间以及起开闭情况!
#include<stdio.h> #include<algorithm> #include<string.h> using namespace std; #define MAXN1 65545<<1 #define MAXN 65535<<1 int x[MAXN<<4],col[MAXN<<4]; void FXOR(int a) { if(x[a]!=-1) x[a]^=1; else col[a]^=1; } void push_down(int a) { if(x[a]!=-1) { x[a<<1]=x[a<<1|1]=x[a]; col[a<<1]=col[a<<1|1]=0; x[a]=-1; } if(col[a]) { FXOR(a<<1); FXOR(a<<1|1); col[a]=0; } } void update(char val,int l,int r,int a1,int b1,int a) { if(l>=a1&&b1>=r) { if(val=='U') { x[a]=1; col[a]=0; } else if(val=='D') { x[a]=0; col[a]=0; } else if(val=='S'||val=='C') FXOR(a); return ; } push_down(a); int mid=(l+r)>>1; if(a1<=mid) update(val,l,mid,a1,b1,a<<1); else if(val=='I'||val=='C') x[a<<1]=col[a<<1]=0; if(b1>mid) update(val,mid+1,r,a1,b1,a<<1|1); else if(val=='I'||val=='C') x[a<<1|1]=col[a<<1|1]=0; } bool vis[MAXN1]; void Query(int l,int r,int a) { if(x[a]==1) { for(int i=l;i<=r;i++) vis[i]=1; return ; } else if(x[a]==0) return ; if(l==r) return ; push_down(a); int mid=(l+r)>>1; Query(l,mid,a<<1); Query(mid+1,r,a<<1|1); } int main() { x[1]=col[1]=0; char a,a1,a2,b; int l,r; while(scanf("%c %c%d,%d%c%c",&a,&a1,&l,&r,&a2,&b)!=EOF) { l<<=1; r<<=1; if(a1=='(') l++; if(a2==')') r--; if(l>r) continue; update(a,0,MAXN,l,r,1); } Query(0,MAXN,1); int s=-1,e; bool flag=0; for(int i=0;i<=MAXN;i++) { if(vis[i]) { if(s==-1) s=i; e=i; } else { if(s!=-1) { if(flag)printf(" "); printf("%c%d,%d%c",s&1?'(':'[',s>>1,(e+1)>>1,e&1?')':']'); s=-1; flag=1; } } } if(!flag) printf("empty set"); return 0; }