Help with Intervals POJ

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


原文地址:https://www.cnblogs.com/Levi-0514/p/9092860.html