POJ3225【线段树+异或、01处理】

题目链接:http://poj.org/problem?id=3225

思路:

我们一个一个操作来分析:(01表示是否包含区间,-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互换

成段覆盖的操作很简单,比较特殊的就是区间0/1互换这个操作,我们可以称之为异或操作
很明显我们可以知道这个性质:当一个区间被覆盖后,不管之前有没有异或标记都没有意义了
所以当一个节点得到覆盖标记时把异或标记清空
而当一个节点得到异或标记的时候,先判断覆盖标记,如果是01,直接改变一下覆盖标记,不然的话改变异或标记

开区间闭区间只要数字乘以2就可以处理(偶数表示端点,奇数表示两端点间的区间)
线段树功能:update:成段替换,区间异或 query:简单hash

 

说实话,自己现在单纯自己思考的话的确还是无法驾驭这种题的,用线段树操作的部分不算难。

主要点在怎么覆盖还有异或的处理上,尤其是异或的处理很让我蛋疼。

Lazy标记得用的炉火纯青,偶还未达到那个高度。难过

另外自己更加注意了一些细节性的东西,比如题目是否可能输入0。 涉及读入%c以及回车的时候要万分小心。

学到一些技巧,比如开闭区间的处理只要乘以2判断奇偶性即可。

 

 

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

const int maxn=131072;

#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r

int Xor[maxn<<2];
int cover[maxn<<2];
int hash[maxn<<2];

void Fxor(int rt)  //取异或@@
{
    if(cover[rt]!=-1)
    {
        cover[rt]^=1;
    }
    else  //cover[rt]==-1说明rt所关联的这块区域既有包含又有不包含 所以直接把这一块区间取异或是不对的
    {
        Xor[rt]^=1;
    }
}
void Pushdown(int rt)
{
    if(cover[rt]!=-1)
    {
        cover[rt<<1]=cover[rt<<1|1]=cover[rt];
        Xor[rt<<1]=Xor[rt<<1|1]=0; //已覆盖,异或标记清零
        cover[rt]=-1;
    }

    //这地方还得有一个判断异或标记。针对cover[rt]=-1的情况
    if(Xor[rt])
    {
        Fxor(rt<<1);
        Fxor(rt<<1|1);
        Xor[rt]=0;
    }
}
void update(int a,int b,char ch,int rt,int l,int r)//记好自己的习惯 l,r是指结点所表示的区间
{
    if(a<=l&&b>=r)
    {
        if(ch=='U')
        {
            cover[rt]=1;
            Xor[rt]=0;
        }
        else if(ch=='D')
        {
            cover[rt]=0;
            Xor[rt]=0;
        }
        else if(ch=='S'||ch=='C') //这地方的技巧 (-8,a),(b,+8)的处理放在了后面
        {
            Fxor(rt);
        }
        return;
    }
    if(l==r) return;
    Pushdown(rt);

    int m=(l+r)>>1;
    if(a<=m) //a,b与l,r混了。囧
    {
        update(a,b,ch,lson);
    }
    else
    {
        if(ch=='I'||ch=='C')
            Xor[rt<<1]=cover[rt<<1]=0;
    }
    if(b>m)
    {
        update(a,b,ch,rson);
    }
    else
    {
        if(ch=='I'||ch=='C')
            Xor[rt<<1|1]=cover[rt<<1|1]=0;
    }
}
void query(int rt,int l,int r)
{
    if(cover[rt]==1)
    {
        for(int i=l;i<=r;i++)
        {
            hash[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()
{
   // freopen("input.txt","r",stdin);
    cover[1]=Xor[1]=0; //初始化
    char op;
    char c1,c2;
    int a,b;
    while(scanf("%c%*c%c%d,%d%c\n",&op,&c1,&a,&b,&c2)!=EOF)//这地方去掉回车就不对!!!
    {
        a*=2;b*=2;
        if(c1=='(') a++;
        if(c2==')') b--;
        if(a>b) //(3,3)感觉这种情况不会出现,出现即全部清零,加上也没错
        {
            cover[1]=0;
            Xor[1]=0;
        }
        else update(a,b,op,1,0,maxn);//操作数可能有0
    }

    query(1,0,maxn);

    bool flag=false;

    //for(int i=0;i<=10;i++) printf("%d\n",hash[i]);
    int temp=1;
    for(int i=0;i<=maxn;i++) //处理多个非连续区间的技巧
    {
        if(hash[i]==1)
        {
            flag=true;
            if(temp==1)
            {
                if(i%2==1) printf("(%d,",i/2);
                else printf("[%d,",i/2);
            }
            temp=2;
        }
        else if(hash[i]==0)
        {
            if(temp==2)
            {
                if((i-1)%2==1) printf("%d)",i/2);
                else printf("%d]",i/2);
                printf(" ");
            }
            temp=1;
        }
    }

    if(!flag)
        printf("empty set\n");
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值