POJ 3225 区间 线段树

LogLoader是一家专门提供日志分析产品的公司。Ikki在做毕业设计的同时,还忙于在LogLoader做实习。在他的工作里,有一项是要写一个模块来处理时间区间。这个事情一直让他感到很迷糊,所以现在他很需要你帮忙。
在离散数学里面,你已经学习了几种基本的集合运算,具体地说就是并、交、相对补和对称差。它们自然地也适用于区间这种特殊的集合。作为你的快速参考,它们可以总结成下表:
运算 记号
定义
并 A ∪ B {x : x ∈ A或x ∈ B}
交 A ∩ B {x : x ∈ A并x ∈ B}
相对补 A − B {x : x ∈ A但是x ∉B}
对称差 A ⊕ B (A − B) ∪ (B − A)
Ikki已经把他的工作里出现的区间运算抽象成一个很小的编程语言。他想你为他实现一个解析器。这个语言维护一个集合S。S一开始是空集,并根据下列命令被修改:
命令 语义
U T S ← S ∪ T
I T S ← S ∩ T
D T S ← S − T
C T S ← T − S
S T S ← S ⊕ T
Input
输入包含一组测试数据,由0到65,535条命令组成。每条命令占一行,形式如下:
X T
其中X是‘U’、‘I’、‘D’、‘C’和‘S’中的一个,T是一个区间,形式为(a,b)、(a,b]、[a,b)和[a,b]之一(a, b ∈ Z; 0 ≤ a ≤ b ≤ 65,535),取它们通常的意义。命令按在输入中出现的顺序执行。
文件结束符(EOF)表示输入结束。
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)

思路:线段树
1.首先我们需要知道这五个操作在线段树中是如何执行的。
U 就是把[l,r]这一段全部变成0 //线段树操作:成段更新
I 就是把[0,l)(r,maxn]这一段全部变成0 //成段更新
D 就是把[l,r]这一段变成0 //成段更新
C 就是把[0,l)(r,maxn]这一段全部变成0(成段更新) 把[l,r] 0/1进行互换(成端更新,异或操作)
S 就是把[l,r] 0/1进行互换(成端更新,异或操作)
好了,我们现在已经理清楚第一步了,涉及成端更新,我们就用到了懒惰标记,这里我们需要维护两个值。
cover数组(标记)维护这一段当前的值(-1表示既有包括也有不包括[即区间不连续],0表示该区间不包括,1表示该区间包括)
XOR数组(标记)维护当前区间是否需要进行异或操作,0表示不需要,1表示不需要。(t^=1).
这两个标记如何处理:
1).当某段区间获得cover标记的话,那么意味这之前这段区间无论有没有xor标记,现在都可以直接清除掉。
2).当某段区间获得xor标记的话,先判断其覆盖标记是0或者1的话,我们直接对该段区间(线段树某节点)进行异或操作,
否则的话,改变异或标记。
以上就是线段树的所有操作了,现在还有一个问题,区间是有开区间和闭区间之分的,我们如何来区分区间的开闭呢。
我们把偶数表示端点,奇数表示两个端点之间的那一段区间即可。
线段树中,0表示[0,0],那么1为(0,1),2为[1,1],3为(1,2),4为[2,2],5为(2,3),6为[3,3]
即i为奇数时表示(i/2,i/2+1)(两个端点之间的区间),i为偶数时表示[i/2,i/2](端点).
大概就这些点了。
public class Main {
    // U [l,r]->1
    // I [0,l)(r,maxn]->0
    // D [l,r]->0
    // C [0,l)(r,maxn]->0 [l,r] 0/1(^)
    // S [l,r] 0/1(^)
    public static final int MAXN=(65535<<1);//大小要开两倍
    public static int[] XOR; //维护异或标志
    public static int[] cover; //维护覆盖标志
    public static boolean[] flag;
    public static void FXOR(int rt){
        if(cover[rt]!=-1){ // 0/1互相替换
            cover[rt]^=1; //整段为0替换成1 整段为1替换成0,若01并存的话,那就更新异或标志
        }else{
            XOR[rt]^=1;//如果该段区间既存在包含又存在不包含的改变异或标志
        }
    }
    //先检查覆盖标志再检查异或标志
    public static 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;
        }
        if(XOR[rt]!=0){ 
            FXOR(rt<<1);
            FXOR(rt<<1|1);
            XOR[rt]=0;
        }
    }
    public static void update(char op,int L,int R,int l,int r,int rt){
        if(L<=l&&r<=R){ 
            switch (op){ //这里就是上面说的 对五种情况进行判断 做不同处理
                case 'U':cover[rt]=1;XOR[rt]=0;break; 
                case 'D':cover[rt]=0;XOR[rt]=0;break;
                case 'S': case 'C':FXOR(rt);
            }
            return;
        }
        pushDown(rt); 
        int mid=(l+r)>>1;
        if(L<=mid){
            update(op,L,R,l,mid,rt<<1); //区间内操作
        }else if(op=='I'||op=='C'){ //I C需要对区间外进行操作
            cover[rt<<1]=XOR[rt<<1]=0;  //区间外变成0
        }
        if(R>mid){
            update(op,L,R,mid+1,r,rt<<1|1);
        }else if(op=='I'||op=='C'){
            cover[rt<<1|1]=XOR[rt<<1|1]=0;//区间外变成0
        }
    }
    public static void query(int l,int r,int rt){
        if(cover[rt]==1){ //进行区间覆盖标志判断
            for(int i=l;i<=r;++i){
                flag[i]=true; //标记该节点所包含的这一段区间
            }
            return;
        }else if(cover[rt]==0){
            return; 
        }
        if(l==r){
            return;
        }
        pushDown(rt);
        int mid=(l+r)>>1;
        query(l,mid,rt<<1);
        query(mid+1,r,rt<<1|1);
    }
    public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
        //Scanner cin=new Scanner( InputUtil.cin());
        Scanner cin=new Scanner(new InputStreamReader(System.in));
        XOR=new int[MAXN<<2];
        cover=new int[MAXN<<2];
        flag=new boolean[MAXN+1];
        cover[1]=XOR[1]=0;
        while (cin.hasNext()){
            String op=cin.next();
            String f=cin.next();
            int a=Integer.parseInt(f.substring(1,f.lastIndexOf(',')));
            int b=Integer.parseInt(f.substring(f.lastIndexOf(',')+1,f.length()-1));
            char l=f.charAt(0);
            char r=f.charAt(f.length()-1);
            a<<=1;b<<=1; //乘以2 映射到线段树中 奇数表示两端点间的那一段区间 偶数表示端点
            if(l=='('){
                ++a;
            }
            if(r==')'){
                --b;
            }
            if(a>b){ // (3,3)
                if(op.charAt(0)=='C'||op.charAt(0)=='I'){
                    XOR[1]=cover[1]=0; //直接整个区间变成0
                }
            }else{
                update(op.charAt(0),a,b,0,MAXN,1);
            }
        }
        query(0,MAXN,1);
        boolean f=false;
        int st=-1;
        int ed=0;
        for(int i=0;i<=MAXN;++i){
            if(flag[i]){
                if(st==-1){
                    st=i;
                }
                ed=i;
            }else{
                if(st!=-1){
                    if(f){
                        System.out.print(" ");
                    }
                    f=true;
                    StringBuilder sb=new StringBuilder();
                    char l=(st&1)==1?'(':'['; //判断节点奇偶性 偶数为闭区间 奇数为开区间
                    char r=(ed&1)==1?')':']'; 
                    sb.append(l).append(st>>1).append(',').append((ed+1)>>1).append(r);
                    //左边是奇数不需要+1 右边是奇数我们需要+1偏向大的区间 [6,7)->[3,4) (5,6]->(2,3]
                    System.out.print(sb.toString());
                    st=-1;
                }
            }
        }
        if(!f){
            System.out.println("empty set");
        }
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值