POJ 3225 Help with Intervals(线段树:区间置0/1,区间异或)
http://poj.org/problem?id=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
TS ← S ∪ T I
TS ← S ∩ T D
TS ← S − T C
TS ← T − S S
TS ← 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)
分析:
找到几个小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();
}