Bzoj 3226: [Sdoi2008]校门外的区间

以下来自 ShallWe's Blog

3226: [Sdoi2008]校门外的区间
Description

  受校门外的树这道经典问题的启发,A君根据基本的离散数学的知识,抽象出5种运算维护集合\(S\)(\(S\)初始为空)并最终输出S。现在,请你完成这道校门外的树之难度增强版——校门外的区间。
  基本集合运算如下:
\(U:A∪B=\{x:x∈A|x∈B\}\)
\(I:A∩B=\{x:x∈A \& x∈B\}\)
\(D:A-B=\{x:x∈A \& x∉B\}\)
\(C:B-A\)
\(S:A⊕B=(A-B)∪(B-A)\)

Input

  输入共M行。
  每行的格式为X T,用一个空格隔开,X表示运算的种类,T为一个区间(区间用\((a,b)\),\((a,b]\),\([a,b)\),\([a,b]\)表示)。

Output

  共一行,即集合\(S\),每个区间后面带一个空格。若S为空则输出"empty set"。
对于\(100%\)的数据,\(0≤a≤b≤65535\),\(1≤M≤70000\)

解题报告

应该说是比较容易(by dada见友联)的一个题,首先,按照高中数学必修1所学的Vane图以及集合的基本运算,可以得到每一种对于区间进行的集合操作的效果:

  1. U: B区间->1
  2. I: 非B区间->0
  3. D:B区间->0
  4. C: 取反、转2
  5. S: 区间取反

请花vane图加深感受.
然后区间取反区间覆盖,当然可以使用线段树、平衡树(?),但是这两种标记在线段树中是不可合并的,但其实无所谓因为在询问的时候没有区间查询,而一旦出现要标记合并的地方,就提前下传,就像iwtwiioi说过“不能合并就传下去”,然后就没什么细节了,输入输出的过程中,将\(()\)作为\(*2+/-1\),\([]\)作为\(*2\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N=131075;
int mark[N<<2],white[N<<2],rev[N<<2],m,n=131071;
char ch[10];
bool flag=0;
inline void in(int &x,int &y){
    char ch=getchar(); int tmp;
    for (;ch<'0'||ch>'9';ch=getchar()){
        if (ch=='(') x=1; 
        if (ch=='[') x=0; 
    }
    for (tmp=0;ch>='0'&&ch<='9';ch=getchar())
        tmp=tmp*10+ch-48;
    x+=tmp<<1;
    for (;ch<'0'||ch>'9';ch=getchar());
    for (tmp=0;ch>='0'&&ch<='9';ch=getchar())
        tmp=tmp*10+ch-48; 
    for (;ch!=']'&&ch!=')';ch=getchar()); 
    if (ch==']') y=0; 
    else y=-1; 
    y+=tmp<<1;
}
void build(int x,int l,int r){
    mark[x]=-1,white[x]=0; 
    if (l==r) return; 
    int mid=(l+r)>>1; 
    build(x<<1,l,mid); 
    build(x<<1|1,mid+1,r); 
}
inline void down(int x,int l,int r){
    int m=mark[x],re=rev[x]; 
    mark[x]=-1,rev[x]=0;
    if (l==r){
        if (m!=-1)
            white[x]=m;
        white[x]^=re;
        return;
    }
    if (m!=-1){
        mark[x<<1]=m;
        mark[x<<1|1]=m;
        rev[x<<1|1]=rev[x<<1]=0; 
    }
    rev[x<<1]^=re,rev[x<<1|1]^=re;
}
        
void add(int x,int l,int r,int L,int R,int val){
    down(x,l,r);    
    if (L<=l&&r<=R){
        if (val<2)
            mark[x]=val; 
        else
            rev[x]^=1; 
        return;
    }
    int mid=(l+r)>>1; 
    if (L<=mid) add(x<<1,l,mid,L,R,val); 
    if (R>mid) add(x<<1|1,mid+1,r,L,R,val); 
}
int query(int x,int l,int r,int pur){
    down(x,l,r);
    if (l==r) return white[x]; 
    int mid=(l+r)>>1; 
    if (pur<=mid) 
        return query(x<<1,l,mid,pur);
    else 
        return query(x<<1|1,mid+1,r,pur);
}
inline void print(int x,int y){
    if (x&1) printf("(%d,",x>>1); 
    else printf("[%d,",x>>1); 
    if (y&1) printf("%d)",y+1>>1); 
    else printf("%d]",y>>1);
    flag=1;
    printf(" ");
}
int main(){
//  freopen("interval.in","r",stdin); 
//  freopen("interval.out","w",stdout);
    build(1,0,n);
    int a,b;
    while (scanf("%s",ch)!=EOF){
        in(a,b);
        if (ch[0]=='U') 
            add(1,0,n,a,b,1);
        if (ch[0]=='I'){
            if(a)
                add(1,0,n,0,a-1,0);
            add(1,0,n,b+1,n,0);
        }
        if (ch[0]=='D') 
            add(1,0,n,a,b,0);
        if (ch[0]=='C'){ 
            add(1,0,n,1,a-1,0);
            add(1,0,n,a,b,2);
            add(1,0,n,b+1,n,0);
        }
        if (ch[0]=='S') 
            add(1,0,n,a,b,2);
    }
    int s=-1,t=0;
    for (t=0;t<=n;t++)
        if (!query(1,0,n,t)){
            if (s!=-1) print(s,t-1),s=-1; 
        }else
            if (s==-1) s=t;
    if (s!=-1) print(s,n),s=-1; 
    if (!flag) printf("empty set");
    return 0; 
}

转载于:https://www.cnblogs.com/ShallWe2000/p/5773604.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值