随笔 - 97, 文章 - 22, 评论 - 81, 引用 - 0
数据加载中……

PKU 3225 Help with Intervals

题目链接:http://poj.org/problem?id=3225
/*
题意:
    刚开始是一个[0, 65535]的集合,要求通过一些集合操作,最后输出最小的
的集合。如果有多个,按结束坐标最小的依次输出,集合操作包括:
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) 并 (T - S) (集合的异或)
T为以下(a,b), (a,b], [a,b), [a,b]四种形式之一,初始集合S为空。

解法:
线段树

思路:
    以上的五种操作,其实都可以转化成线段树的区间并操作,首先我们建立一
颗线段树,对应以上的区间,我们注意到这里的区间有开有闭,为了方便处理,
可以将每个区间值乘上2,比如当前读入的区间时(a,b],那么实际处理的其实是
[2*a+1, 2*b]这个区间,这样做的好处是无论开闭都能映射到整点。然后再来看
集合操作如何转化成线段树的区间操作,首先要知道这些集合如何和线段树的区
间一一对应,我的做法是在线段树的区间上用01来表示当前集合的元素是否存在
。具体的对应如下:
    集合的并:S 并 T,要求S中的元素仍就存在,并且引入S中没有的而且在T中
的元素,我们只要在T区间插入一段连续的1即可。
    集合的交:S 交 T,要求在S中不在T中的元素全部剔除,那么其实就是在T的
补区间内插入一段连续的0即可。
    集合的差:S - T,这个形式其实可以表示成 S 交 非T,于是可以转换成第二
种形式,就是在非T的补区间也就是T中插入一段连续的0。
    集合的逆差:T - S,这个形式可以表示成 非S 交 T,那么可以先将S中的元
素进行异或,然后再在T的补区间内插入一段连续的0。
    集合的对称集:S 异或 T,如果S和T中都存在那么就将他们去掉,否则只要
有一个存在就留下来。只要在T区间内做一次异或操作即可。
    这样一来完全对应了线段树的操作,归根到底只有三种插入操作:插入一段连
续的0,插入一段连续的1,将某段区间的值均和1异或。而查询操作是最后的依次
询问。而这三种操作可以参见下面这题的题解:
http://www.cppblog.com/menjitianya/archive/2011/04/02/143265.html
*/


#include 
<iostream>

using namespace std;

#define maxn 65535*2


enum Lazy_Tag {
    ZERO    
= 0,
    ONE     
= 1,
    XOR     
= 2,
    TAG_MAX 
= 3,
}
;

struct Interval {
    
char operands;
    
int left, right;
    Interval() 
{}
    Interval(
char op, int l, int r) {
        operands 
= op;
        left 
= l;
        right 
= r;
    }

}
I[maxn*5];

char id[] = {'U''I''D''C''S'};

int find(char c) {
    
for(int i = 0; i < 5; i++{
        
if(c == id[i])
            
return i;
    }

}


struct Tree {
    
char lazy[TAG_MAX];
    
int Sum;
    
int root, l, r;

    
void CoverBy(int mode);
    
void UpdateBy(Tree* ls, Tree* rs);
    
void TranslateToSon();
    
void TranslateTo(Tree* ts);

    
int len() {
        
return r - l + 1;
    }

}
T[maxn*4];
int v[maxn+1];


void Build(int root, int l, int r) {
    T[root].l 
= l;
    T[root].r 
= r;
    T[root].root 
= root;
    T[root].Sum 
= 0;
    
int i;
    
for(i = 0; i < TAG_MAX; i++)
        T[root].lazy[i] 
= 0;

    
if(l == r)
        
return ;

    
int mid = (l + r) >> 1;
    Build(root
<<1, l, mid);
    Build(root
<<1|1, mid+1, r);
}


void Tree::UpdateBy(Tree* ls, Tree* rs) {
    Sum 
= ls->Sum + rs->Sum;
}


void Tree::CoverBy(int mode) {
    
if(mode == ZERO || mode == ONE) {
        Sum 
= len() * mode;
        lazy[mode] 
= 1;
        lazy[mode
^1= lazy[XOR] = 0;
    }
else {
        Sum 
= len() - Sum;
        
if(lazy[ONE]) {
            lazy[ONE] 
= 0;
            lazy[ZERO] 
= 1;
        }
else if(lazy[ZERO]) {
            lazy[ZERO] 
= 0;
            lazy[ONE] 
= 1;
        }
else
            lazy[XOR] 
^= 1;
    }

}


void Tree::TranslateToSon() {
    TranslateTo(
&T[root<<1]);
    TranslateTo(
&T[root<<1|1]);
    
for(int i = 0; i < TAG_MAX; i++)
        lazy[i] 
= 0;
}


void Tree::TranslateTo(Tree* ts){
    
for(int i = 0; i < TAG_MAX; i++{
        
if(lazy[i]) {
            ts
->CoverBy(i);
        }

    }

}



void Insert(int root, int l, int r, int mode) {
    
if(l > T[root].r || r < T[root].l)
        
return ;

    
if(mode != XOR && T[root].lazy[mode]) {
        
return ;
    }


    
if(l <= T[root].l && T[root].r <= r) {
        T[root].CoverBy(mode);
        
return ;
    }

    T[root].TranslateToSon();
    Insert(root
<<1, l, r, mode);
    Insert(root
<<1|1, l, r, mode);

    T[root].UpdateBy(
&T[root<<1], &T[root<<1|1]);
}


int Query(int root, int pos) {
    
if(pos > T[root].r || pos < T[root].l)
        
return 0;
    
if(pos <= T[root].l && T[root].r <= pos) {
        
return T[root].Sum;
    }

    T[root].TranslateToSon();
    
return Query(root<<1, pos) + Query(root<<1|1, pos);
}


int main() {
    
char str[2][100];
    
int size = 0;
    
int i, j;

    
while(scanf("%s %s", str[0], str[1]) != EOF) {
        
int len = strlen(str[1]);
        
int a[2= {00}, top = 0;
        
for(i = 1; str[1][i]; i++{
            
if(str[1][i] >= '0' && str[1][i] <= '9'{
                a[top] 
= a[top] * 10 + (str[1][i] - '0');
            }
else if(str[1][i] == ','{
                top
++;
            }

        }

        a[
0*= 2; a[1*= 2;
        
if(str[1][0== '(') a[0++;
        
if(str[1][len-1== ')') a[1]--;

        I[size
++= Interval(find(str[0][0]), a[0], a[1]);
    }

    Build(
10, maxn);

    
for(i = 0; i < size; i++{
        
if(I[i].operands == 0{
            
// 集合并
            Insert(1, I[i].left, I[i].right, 1);
        }
else if(I[i].operands == 1{
            
// 集合交
            Insert(10, I[i].left - 10);
            Insert(
1, I[i].right+1, maxn, 0);
        }
else if(I[i].operands == 2{
            
// 集合差
            Insert(1, I[i].left, I[i].right, 0);
        }
else if(I[i].operands == 3{
            
// 集合逆差
            Insert(10, I[i].left - 10);
            Insert(
1, I[i].right+1, maxn, 0);
            Insert(
1, I[i].left, I[i].right, 2);
        }
else if(I[i].operands == 4{
            
// 集合的对称集
            Insert(1, I[i].left, I[i].right, 2);
        }

    }

    
int Min = INT_MAX;
    size 
= 0;

    
for(i = 0; i <= maxn; i++{
        v[i] 
= Query(1, i);
    }


    
for(i = 0; i <= maxn; i++{
        
if(v[i]) {
            
for(j = i + 1; j <= maxn; j++{
                
if(!v[j])
                    
break;
            }

            I[size
++= Interval(0, i, j-1);
            i 
= j;
        }

    }


    
if(size == 0{
        printf(
"empty set\n");
    }
else {
        
for(i = 0; i < size; i++{
            
if(i)
                printf(
" ");
            printf(
"%c%d,%d%c", I[i].left&1?'(':'[', I[i].left/2, (I[i].right+1)/2, I[i].right&1?')':']');
        }

        puts(
"");
    }

    
return 0;
}


/*
U (1,65535)
D (100,1000]

U (0,65535)
D (10,65535)
D [10,10]
U (65525,65535]
U [0,0]
*/

posted on 2011-04-02 22:50 英雄哪里出来 阅读(1498) 评论(2)  编辑 收藏 引用 所属分类: 线段树

评论

# re: PKU 3225 Help with Intervals  回复  更多评论   

看你的解题报告就是容易明白,感谢啊!!
2011-09-07 10:24 | 卡卡

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理