POJ 3225 Help with Intervals(线段树:区间置0/1,区间异或)

POJ 3225 Help with Intervals(线段树:区间置0/1,区间异或)

http://poj.org/problem?id=3225

题意:

LogLoader是一家专门提供日志分析产品的公司。Ikki在做毕业设计的同时,还忙于在LogLoader做实习。在他的工作里,有一项是要写一个模块来处理时间区间。这个事情一直让他感到很迷糊,所以现在他很需要你帮忙。

在离散数学里面,你已经学习了几种基本的集合运算,具体地说就是并、交、相对补和对称差。它们自然地也适用于区间这种特殊的集合。作为你的快速参考,它们可以总结成下表:

运算 记号

定义

A ∪ B {x : x ∈ Ax ∈ B}
A ∩ B {x : x ∈ Ax ∈ B}
相对补 A − B {x : x ∈ A但是x ∉B}
对称差 A ⊕ B (A − B) ∪ (B − A)

Ikki已经把他的工作里出现的区间运算抽象成一个很小的编程语言。他想你为他实现一个解析器。这个语言维护一个集合SS一开始是空集,并根据下列命令被修改:

命令 语义
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]之一(ab ∈ 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)

分析:

  找到几个小BUG,直接AC了,还是需要写出自己的所有思路,才比较好写代码.

   首先这道题目要处理区间,所以我们在线段树的各个节点用0表示不包括,1表示包括,-1表示子节点中既有包括又有不包括.下面分析对应的5种操作,假设T表示的区间是[l,r]:

U T: [l,r]区间置1

I T: [0,l)和(r,max]置0

D T: [l,r]置0

C T: [0,l)和(r,max]置0 , [l,r]区间中0变1,1变0(异或操作)

S T:[l,r]区间0/1互换(异或)


由于区间有开区间和闭区间,所以将所有区间映射到整数上去,例如(仔细想想下面的举例):

第0号元素代表区间[0,0],1号元素代表(0,1),2号元素代表[1,1],3号元素代表(1,2),4号元素代表[2,2]区间。如果i为偶数时i代表区间[i/2,i/2],i为奇数时i代表(i/2,i/2+1)区间。如果给你一个原始区间(L,R],那么这个区间映射的代表整数为L*2+1到R*2之间,即线段树某个节点维护的元素区间[L*2+1,R*2]。


线段树节点维护cover信息,0表示不包括,1表示包括,-1表示子节点既有包括的又有不包括的.

然后还要维护一个异或信息XOR,XOR表示1的时候表示该区间需要异或,否则为0时表不需要异或.

建树build: 由于原来节点最多65535个,节点翻倍为131070个,然后线段树取4倍的131070个节点即可.build其实可以不用直接用memset把XOR和cover全设置为0即可.

更新update_1(将给定的区间置1或0):由于每个节点都有XOR和cover信息.如果待修改区间包含了update_1当前扫描的节点区间,那么直接置XOR=0,cover=1即可.如果没有完全包含,那么需要PushDown操作.然后分段处理.分段处理之后还需要PushUp操作.

每个节点有XOR信息和cover信息,这两种信息的存在情况如下:如果cover!=-1,那么XOR必为0,也就是说只要cover不为-1,来了XOR操作直接更新.否则如果cover==-1,那么XOR才不一定为0.

PushDown操作:cover!=-1时,直接PuchDown cover信息即可.否则cover==-1时2,需要PushDown XOR信息,异或i节点原有的XOR信息.

更新update_2(将给定的区间异或):由于每个节点都有XOR和cover信息.如果待修改区间包含了update_2当前扫描的节点区间,那么直接置对原本的XOR执行异或操作即可.如果没有完全包含,那么需要PushDown操作.然后分段处理.分段处理之后还需要PuchUp操作.

PushUp操作:本操作主要是为了维护cover信息,XOR信息位已经下放了不用管.

query操作:本操作就是按遍历一遍所有加倍后的节点,然后用hash标记,之后我们利用一次循环扫描输出所有区间信息.

T的区间翻倍处理后是[l,r],下面5种情况为:

U T: [l,r]区间置1

I T: [0,l-1]和[r+1,max]置0

D T: [l,r]置0

C T: [0,l-1]和[r+1,max]置0 , [l,r]区间中0变1,1变0(异或操作)

S T:[l,r]区间0/1互换(异或)

这里要注意:如果update的参数ql>qr,那么直接返回,不处理.

AC代码:1172ms

#include
#include
#include
using namespace std;
const int MAXN = 65535 * 2 + 2;
#define lson i*2,l,m
#define rson i*2+1,m+1,r
#define root 1,0,MAXN
int cover[MAXN * 4];
int XOR[MAXN * 4];
bool hash[MAXN + 1];
void FXOR(int i)
{
    if(cover[i] != -1)//cover 不为-1,那么XOR必为0
    {
        cover[i] ^= 1;
        XOR[i] = 0;
    }
    else
        XOR[i] ^= 1;
}
void PushUp(int i)
{
    int lc = i * 2, rc = i * 2 + 1;
    if(cover[lc] == -1 || cover[rc] == -1)
        cover[i] = -1;
    else if(cover[lc] == cover[rc])
        cover[i] = cover[lc];
    else
        cover[i] = -1;
}
void PushDown(int i)
{
    int lc = i * 2, rc = i * 2 + 1;
    if(cover[i] != -1)
    {
        cover[lc] = cover[rc] = cover[i];
        XOR[lc] = XOR[rc] = 0;
    }
    else if(XOR[i] == 1)
    {
        XOR[i] = 0;
        FXOR(rc);//对节点rc执行异或操作
        FXOR(lc);
    }
}
void update_1(int ql, int qr, int v, int i, int l, int r)
{
    if(ql > qr)return ;
    if(ql <= l && r <= qr)
    {
        cover[i] = v;
        XOR[i] = 0;
        return ;
    }
    PushDown(i);
    int m = (l + r) / 2;
    if(ql <= m) update_1(ql, qr, v, lson);
    if(m < qr) update_1(ql, qr, v, rson);
    PushUp(i);
}
void update_2(int ql, int qr, int i, int l, int r)
{
    if(ql > qr) return ;
    if(ql <= l && r <= qr)
    {
        FXOR(i);
        return ;
    }
    PushDown(i);
    int m = (l + r) / 2;
    if(ql <= m) update_2(ql, qr, lson);
    if(m < qr) update_2(ql, qr, rson);
    PushUp(i);
}
void query(int i, int l, int r)
{
    if(cover[i] == 1)
    {
        for(int j = l; j <= r; j++)
            hash[j] = true;
        return ;
    }
    else if(cover[i] == 0) return;
    if(l == r)return ; //此句可以不要
    PushDown(i);
    int m = (l + r) / 2;
    query(lson);
    query(rson);
}
void solve()
{
    memset(hash, 0, sizeof(hash));
    query(root);
    int s = -1, e;
    bool flag = true; //表示是否是第一个输出的区间
    for(int i = 0; i <= MAXN; i++)
    {
        if(hash[i])
        {
            if(s == -1)s = i;
            e = i;
        }
        else
        {
            if(s != -1)
            {
                if(!flag) printf(" ");
                flag = false;
                printf("%c%d,%d%c", s % 2 == 1 ? '(' : '[', s / 2, (e + 1) / 2, e % 2 == 1 ? ')' : ']');
                //printf("%d %d\n",s,e);
                s = -1;
            }
        }
    }
    if(flag)printf("empty set");
    puts("");
}
int main()
{
    memset(cover, 0, sizeof(cover));
    memset(XOR, 0, sizeof(XOR));
    char op, L, R;
    int l, r;
    while(scanf("%c %c%d,%d%c\n", &op , &L , &l , &r , &R)==5)
    {
        l = l * 2;
        r = r * 2;
        if(L == '(')l++;
        if(R == ')')r--;
        if(op == 'U')
            update_1(l, r, 1, root);
        else if(op == 'I')
        {
            update_1(0, l - 1, 0, root);
            update_1(r + 1, MAXN, 0, root);
        }
        else if(op == 'D')
            update_1(l, r, 0, root);
        else if(op == 'C')
        {
            update_1(0, l - 1, 0, root);
            update_1(r + 1, MAXN, 0, root);
            update_2(l, r, root);
        }
        else if(op == 'S')
            update_2(l, r, root);
    }
    solve();
}


你可能感兴趣的:(注意!,★★★,数据结构--线段树,need,to,review,ACM--题解汇总,practice,again,ACM算法竞赛入门经典题解)