【bzoj3226】[Sdoi2008]校门外的区间

Description

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

  基本集合运算如下:
A∪B:{x : xÎA or xÎB}
A∩B:{x : xÎA and xÎB}
A-B:{x : xÎA and xÏB}
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”。

Sample Input

U [1,5]
D [3,3]
S [2,4]
C (1,5)
I (2,3]

Sample Output

(2,3)

HINT

对于 100% 的数据,0≤a≤b≤65535,1≤M≤70000

Source
线段树


解题报告:

开闭区间取决于符号
那么我可以把(2,3]变成[2.5,3]
最后处理2.5就行了
为了方便,我们把整体乘二,最后讨论奇偶性来输出

并集是添加元素,输入[a,b]把a~b的元素赋值为1

交集是只能保留与输入范围重复的集合
输入范围重复代表输入范围以外全都要删去
则输入[a,b]就把1~a-1,b+1~65536全赋值为0

相减:A-B。则B中有的元素A中不能有
读入[a,b]则把a~b的元素全赋值为0

B-A。则A中有的元素B中不能有
则输入[a,b],把1~a-1,b+1~65536全赋值为0
再把a~b的元素取反

(A-B)∪(B-A)。
A-B是将1~a-1,b+1~65536的元素保留,B-A是将1~a-1,b+1~65536的元素清除,然后a~b的元素取反。
结合起来就是将a~b的元素取反。

如何用线段树实现
我们可在叶子节点存储是否有该元素,然后用col[]数组表示是否清零或添加
用rev[]数组表示是否取反
只在子节点存储0 1情况


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define M 65600
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define n (M*2) //开成两倍大小
using namespace std; 
int col[n<<2],rev[n<<2],sum[n<<2];//范围也要改变 
int read()
{
    int x=0,f=0;char ch;
    ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='(')f=-1;
    ch=getchar();}
    while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    if(ch==')')f=1;
    return x*2-f;
}
void build(int l,int r,int rt)
{
    col[rt]=-1;
    rev[rt]=0;
    if(l==r){return;}
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
}
void pushdown(int rt,bool fl,int l)
{
    if(fl)
    {
        if(col[rt]!=-1)sum[rt]=col[rt];
        sum[rt]^=rev[rt];
        rev[rt]=0;col[rt]=-1;
        return;//return一定要有 
    }
    if(col[rt]!=-1)
    {
        col[rt<<1]=col[rt];col[rt<<1|1]=col[rt];
        rev[rt<<1]=0;rev[rt<<1|1]=0;
    }
    rev[rt<<1]^=rev[rt];
    rev[rt<<1|1]^=rev[rt];
    rev[rt]=0;col[rt]=-1;
}
void update(int L,int R,int c,int l,int r,int rt)
{
    if(R<L)return;
    pushdown(rt,(l==r),l);
    if(L<=l && R>=r)
    {
        if(c==-1)rev[rt]^=1;
        else col[rt]=c;
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid)update(L,R,c,lson);
    if(R>mid)update(L,R,c,rson);
}
int query(int c,int l,int r,int rt)
{
    pushdown(rt,(l==r),l);
    if(l==r)return sum[rt];
    int mid=(l+r)>>1;
    if(c<=mid)return query(c,lson);
    else return query(c,rson);
}
int main()
{
    memset(col,-1,sizeof(col));
    memset(sum,0,sizeof(sum));
    memset(rev,0,sizeof(rev));
    char ch1[50];
    while(scanf("%s",ch1)!=EOF)
    {
        int a,b;
        a=read();b=read();
        a+=2;b+=2;
        //cout<<ch1[0]<<" "<<a<<" "<<b<<endl;
        switch(ch1[0])
        {
            case 'U':update(a,b,1,1,n,1);break;
            case 'I':update(1,a-1,0,1,n,1);update(b+1,n,0,1,n,1);break;//case一定要有break!!! 
            case 'D':update(a,b,0,1,n,1);break;
            case 'C':update(1,a-1,0,1,n,1);update(b+1,n,0,1,n,1);update(a,b,-1,1,n,1);break;
            case 'S':update(a,b,-1,1,n,1);break;
        }
    }
    int l=-1,r=-1,flag=0;
    for(int i=1;i<=n;i++)
    {
        if(query(i,1,n,1))
        {
            if(l==-1)l=i;
            r=i;
        }
        else
        {
            if(l!=-1)
            {
                if(flag==1)printf(" ");
                else flag=1;
                if(l&1)printf("(");
                else printf("[");
                printf("%d",l/2-1);
                printf(",%d",(r+1)/2-1);//通过数学方法可知 
                if(r&1)printf(")");
                else printf("]");
                l=-1;r=-1;
            }
        }
    }
    if(flag==0)printf("empty set");
    return 0;//不能打回车 
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值