Help with Intervals POJ - 3225
这题真的是困扰了我好久!!!(最后还是看了胡浩dalao的博客才写出来,不过,有一点的改变)
题意:刚开始,有一个空集S,现给出以下操作(T 表示一个集合)
U T :S与T取并集。
I T :S与T取交集。
D T :S对T取补集。
C T :T对S取补集。
S T :S对T取补集,T对S取补集,然后这两者取并集。
(对集合的交,并,补不太清楚的可以去看一下高中课本,或者百度以下,定义很简单的)
思路:我们可以用点的覆盖来表示集合S,那么,对于各个操作,我们就可以定义为
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互换就是亦或操作。
不过,还有一个难点就是,怎么表示开区间和闭区间呢?
其实,只要把左右区间都乘上2(偶数表示端点,奇数表示两端点间的区间)就可以解决这个问题了。最后只需判断区间的奇偶性,就可以判断是区间还是闭区间。
那么,回到问题,由于有两种操作,所以我们需要准备两个lazy标记,一个cover表示覆盖,一个Xor表示取亦或
还有一点需要注意的是,如果对于一个区间,我们覆盖掉他了,那么之前,对于这个区间的所有亦或操作都是无用的。所以,每覆盖掉一个区间,我们都需要将这个区间的亦或标记清空!
#include<iostream> #include<cstdio> using namespace std; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 const int Max=131073; int cover[Max<<2],Xor[Max<<2],flag[Max];//两个lazy标记,flag记录最后的区间覆盖情况 void PushDown(int rt){//将lazy标记传向下传递 if(cover[rt]!=-1){ cover[rt<<1]=cover[rt<<1|1]=cover[rt]; Xor[rt<<1]=Xor[rt<<1|1]=0;//清空亦或标记 cover[rt]=-1; } else if(Xor[rt]){//亦或操作,如果区间被覆盖,那么,直接对区间取亦或.区间没被覆盖,取亦或无效,所以对Xor标记亦或 if(cover[rt<<1]!=-1) cover[rt<<1]^=1; else Xor[rt<<1]^=1; if(cover[rt<<1|1]!=-1) cover[rt<<1|1]^=1; else Xor[rt<<1|1]^=1; Xor[rt]=0; } } void update(int x,int L,int R,int l,int r,int rt){ if(L<=l&&r<=R){ cover[rt]=x; Xor[rt]=0; return; } if(l==r) return; PushDown(rt); int m=(l+r)>>1; if(L<=m) update(x,L,R,lson); if(R>m) update(x,L,R,rson); } void FXOR(int L,int R,int l,int r,int rt){ if(L<=l&&r<=R){//同理,区间被覆盖则直接亦或,否则,对自己亦或 if(cover[rt]!=-1) cover[rt]^=1; else Xor[rt]^=1; return ; } if(l==r) return ; PushDown(rt); int m=(l+r)>>1; if(L<=m) FXOR(L,R,lson); if(R>m) FXOR(L,R,rson); } void query(int l,int r,int rt){ if(cover[rt]==1){//flag记录区间覆盖情况 for(int i=l;i<=r;i++) flag[i]=1; return ; } else if(cover[rt]==0) return; if(l==r) return; PushDown(rt); int m=(l+r)>>1; query(lson); query(rson); } int main(){ char ch,a,b; int l,r; while(~scanf("%c %c%d,%d%c",&ch,&a,&l,&r,&b)){ getchar(); l<<=1,r<<=1; if(a=='(') l++; if(b==')') r--; if(ch=='U') update(1,l,r,0,Max,1);//分别对应思路里面提到的操作 else if(ch=='I'){ update(0,0,l-1,0,Max,1); update(0,r+1,Max,0,Max,1); } else if(ch=='D') update(0,l,r,0,Max,1); else if(ch=='C'){ FXOR(l,r,0,Max,1); update(0,0,l-1,0,Max,1); update(0,r+1,Max,0,Max,1); } else FXOR(l,r,0,Max,1); } query(0,Max,1); bool f=0; int s=-1,e;//记录答案区间的起点的终点 for(int i=0;i<=Max;i++){ if(flag[i]){ if(s==-1) s=i; e=i; } else{ if(s!=-1){ if(f) printf(" "); f=1; printf("%c%d,%d%c",s&1?'(':'[',s>>1,(e+1)>>1,e&1?')':']');//判断区间的起点的终点的奇偶性,分别对应开区间和闭区间 s=-1;//由于最开始乘2了,所以最后要除2. } } } if(!f) printf("empty set"); puts(""); return 0; }