POJ 3225 Help with Intervals……终于A啦!!!

一个AC一把泪……(看了几篇大牛的博客才搞定)

这个题目就两个关键,搞明白就没什么问题:

1.关于集合运算的推导规约,知道集合是什么东西就一定会推导 

    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互换

2.倍化区间处理开闭区间的问题因为普通的线段树实际处理的并非真正的区间,而是一系列点,相当于处理一个向量这个问题需要处理的是真正的区间,所以应该有一个主导思想就是,把区间点化!不知哪位大牛搞了一个倍增区间出来,实在佩服!对于待处理区间[a,b](暂时不考虑开闭),对其边界均乘2。若区间左开则对左界值+1,若区间右开,则对右界-1!

    如:[2,3]会倍增为[4,6],[2,3)会倍增为[4,5],(2,3]会倍增为[5,6],(2,3)将倍增为[5,5],我们这时可以看到,对于普通线段树无法处理的线段如(x,x+1)将被点化为[2*x+1,2*x+1]!这个问题得到比较完美的解决

最后把查找出来的区间逆向倍增操作一下,就可以得到实际的区间以及起开闭情况!

代码中还将用到延迟更新,向子节点更新操作时,这个具体纠结在互换上面,不过仔细想想还是容易理解的,下面代码会有注解!

区间倍增后,就是处理普通线段树了,这时候一定要思路清晰!

当然普通线段树有两种,1).一种是一开一闭的形式,兄弟节点看上去好像连接起来了(由于开闭是固定的,所以无法直接来处理该题):如对于父节点[a,b),其左孩子为[a,(a+b)/2),右孩子为[(a+b)/2,b)……2).另一种是全闭的形式,对于父节点[a,b],其左孩子为[a,(a+b)/2],右孩子为[(a+b)/2+1,b]……这时候一定要看清本质,不要被倍增区间搞混了!

注:题目中还要处理一些无效输入如(4,4)这种没有意义的区间

代码如下:

/*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互换*/

#include<cstdio>
#include<iostream>
using namespace std;

#define MAXLEN 65536<<1     //倍增区间长度
int Cover[MAXLEN<<2];     //4倍的线段树的覆盖标记空间
int Reversal[MAXLEN<<2]; //4倍的线段树的翻转标记空间
struct
{
    char l, r;
    int a, b;
}Interval[MAXLEN+1];    //区间序列    
int Count;    //区间计数器        

void push_down(int num)    //延迟更新中用于向子节点更新信息
{
    if(Cover[num]!=-1)    //当前节点获得覆盖标记
    {
        Cover[num<<1]=Cover[num<<1|1]=Cover[num];    //覆盖标记传递给子节点
        Reversal[num<<1]=Reversal[num<<1|1]=0;    //清除子节点翻转标记
        Cover[num]=-1;    //当前节点执行覆盖完成,清除覆盖标志
    }
    if(Reversal[num])    //当前节点获得翻转标记
    {
        if(Cover[num<<1]!=-1) Cover[num<<1]^=1;    //左子节点持有覆盖标记,覆盖标记取反    
        else Reversal[num<<1]^=1;    //翻转标记取反
        if(Cover[num<<1|1]!=-1)    Cover[num<<1|1]^=1;    
        else Reversal[num<<1|1]^=1;    
        Reversal[num]=0;    //当前节点执行翻转完成,清除翻转标志
    }
}

void update_info(int op,int l,int r,int LB,int RB,int num)
{
    if( l > r  || l < 0) return ;    //异常区间直接返回
    if(l==LB&&r==RB)    
    {
        if(op!=-1)    //当前操作为覆盖操作,直接覆盖
        {
            Cover[num]=op;
            Reversal[num]=0;
        }
        else    //当前操作为翻转操作
        {
            if(Cover[num]!=-1) Cover[num]^=1;
            else Reversal[num]^=1;
        }
        return ;
    }
    push_down(num);    //当前节点信息向下更新
    int MID=(LB+RB)>>1;
    if(r<=MID) update_info(op,l,r,LB,MID,num<<1);    //修正左子树
    else if(l>=MID+1)    update_info(op,l,r,MID+1,RB,num<<1|1);    //修正右子树
    else
    {
        update_info(op,l,MID,LB,MID,num<<1);    
        update_info(op,MID+1,r,MID+1,RB,num<<1|1);    
    }
}

void Query(int LB,int RB,int num)    //区间查询
{
    if(Cover[num]==1)
    {
        if(LB%2) Interval[Count].l='(';
        else Interval[Count].l='[';
        Interval[Count].a=LB>>1;    //此处左开+1在右移除2时自动丢掉了
        if(RB%2) Interval[Count].r=')';
        else Interval[Count].r=']';
        Interval[Count].b=(RB+1)>>1;    //此处+1是补偿右开时的-1
        Count++;
    }
    else if(Cover[num]==0) return;
    else
    {
        push_down(num);
        int MID=(LB+RB)>>1;
        Query(LB,MID,num<<1);
        Query(MID+1,RB,num<<1|1);
    }
}

void print(void)    //输出区间
{
    char l,r;
    int a,b,i=0;
    if(Count==0) cout<<"empty set"<<endl;
    else
    {
        l=Interval[0].l;
        r=Interval[0].r;
        a=Interval[0].a;
        b=Interval[0].b;
        for(i=1;i<Count;i++)
        {
            if(b==Interval[i].a&&(r==']'||Interval[i].l=='['))
            {
                b=Interval[i].b;
                r=Interval[i].r;
            }
            else
            {
                cout<<l<<a<<','<<b<<r<<' ';
                l=Interval[i].l;
                r=Interval[i].r;
                a=Interval[i].a;
                b=Interval[i].b;
            }
        }
        cout<<l<<a<<','<<b<<r<<' ';
    }
}

int main(int argc,char* argv)
{
    int a,b;
    char op,l,r;
    Cover[1]=Reversal[1]=0;    //初始化树
    while ( ~scanf("%c %c%d,%d%c\n",&op , &l , &a , &b , &r) ) //返回EOF时结束(对值的补码取反)
    {        
        a<<=1;    //区间倍增
        b<<=1;    //区间倍增
        if(l=='(') a++;    //左开+1
        if(r==')') b--;    //右开-1

        if(op=='U') update_info(1,a,b,0,MAXLEN,1);    //集合覆盖
        else if(op=='I')    //集合交集
        {
            update_info(0,0,a-1,0,MAXLEN,1);
            update_info(0,b+1,MAXLEN,0,MAXLEN,1);
        }
        else if(op=='D') update_info(0,a,b,0,MAXLEN,1);    //集合正差    
        else if(op=='C')    //集合逆差        
        {
            update_info(0,0,a-1,0,MAXLEN,1);
            update_info(0,b+1,MAXLEN,0,MAXLEN,1);
            update_info(-1,a,b,0,MAXLEN,1);
        }
        else update_info(-1,a,b,0,MAXLEN,1);    //集合异或
    }
    Query(0,MAXLEN,1);
    print();
    return 0;
}

PS:没看清题目让结果用空格隔开,我隔行输出,果断PE了!幸亏没有报WA,不然我可能到死也不知道哪里错了……一定要读清题目!

转载于:https://my.oschina.net/llmm/blog/124256

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值