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;
}