题目链接:http://poj.org/problem?id=3225
题意:输入为一个操作加一个区间。初始区间为空,求所有操作完后,现在的区间。
输入例如x T。 x为操作,T为区间。
x可以为U,D,S,I,C这五种。
用S表示当前区间。
U表示 S = S ∪ T。
I 表示 S = S ∩ T。
D表示 S = S ? T。
C表示 S = T ? S。
S表示 S = S ⊕ T。
区间可以为如下形式(a,b),(a,b],[a,b),[a,b]。
思路:
很容易想到可以用线段树的区间查询来做。所以可以将区间用0表示不包含,1表示包含,-1表示部分包含。但是因为考虑到线段树表示的为一个点,类似于(2,3)这样的区间,查询时将会是空,因为区间端点均不包含在内。但实际查询时,这个区间是需要输出的。所以此时就需要将区间化为点,可以用倍增区间实现。
(l, r) --> [2 * l + 1, 2 * r - 1]
(l, r] --> [2 * l + 1, 2 * r]
[l, r) --> [2 * l, 2 * r - 1]
[l, r] --> [2 * l, 2 *r]
这样做的目的就是可以将(l, l + 1)这样的区间化为一个点以方便查询。
接下来对于每种操作,即化为对应的更新即可。因为倍增区间后,所有的区间已经化作为闭区间。
U [l, r]:将区间[l, r]置为1
I [l, r]:将区间[0, l - 1]和[r + 1, N]置为0
D [l, r]:将区间[l, r]置为0
C [l, r]:将区间[0, l - 1]和[r + 1, N]置为0,将区间[l, r]反转
S [l, r]:将区间[l, r]反转
最后只需要每次更新最后查询即可。然后再将区间转换为倍增前的区间。
注意
1. 区间可能不合法,即倍增后l>r。这种情况即视区间为空集即可。
2. 有2种类型更新,所以对每种更新需要分别开一个lazy数组。
代码如下
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iomanip>
#include <time.h>
using namespace std;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
const int N = 65535 << 1;
int inter[N << 2];
int cover[N << 2];//赋值lazy
int XX[N << 2];//异或lazy
bool vis[N << 2];
void pushup(int rt) {
if (inter[rt << 1] == inter[rt << 1 | 1])
inter[rt] = inter[rt << 1];
else
inter[rt] = -1;
}
void pushdown(int rt) {
if (cover[rt] != -1) {
XX[rt << 1] = XX[rt << 1 | 1] = 0;//如果赋值,本需要反转的则不需反转
cover[rt << 1] = cover[rt << 1 | 1] = cover[rt];
inter[rt << 1] = inter[rt << 1 | 1] = cover[rt];
cover[rt] = -1;
}
if (XX[rt]) {
if (cover[rt << 1] != -1)//若需赋值,则反转赋值的。
cover[rt << 1] = 1 - cover[rt << 1];
else//否则记录反转lazy
XX[rt << 1] = 1 - XX[rt << 1];
if (inter[rt << 1] != -1)
inter[rt << 1] = 1 - inter[rt << 1];
if (cover[rt << 1 | 1] != -1)
cover[rt << 1 | 1] = 1 - cover[rt << 1 | 1];
else
XX[rt << 1 | 1] = 1 - XX[rt << 1 | 1];
if (inter[rt << 1 | 1] != -1)
inter[rt << 1 | 1] = 1 - inter[rt << 1 | 1];
XX[rt] = 0;
}
}
void build(int l, int r, int rt) {
XX[rt] = 0;
cover[rt] = -1;
inter[rt] = 0;
if (l == r)
return ;
int m = (l + r) >> 1;
build(lson);
build(rson);
}
void update(int o, int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) {
if (o == 2) {
if (cover[rt] != -1)
cover[rt] = 1 - cover[rt];
else
XX[rt] = 1 - XX[rt];
if (inter[rt] != -1)
inter[rt] = 1 - inter[rt];
}
else {
XX[rt] = 0;
cover[rt] = o;
inter[rt] = o;
}
return ;
}
if (l == r)//可能传入了非法区间?不加即RE
return;
pushdown(rt);
int m = (l + r) >> 1;
if (L <= m)
update(o, L, R, lson);
if (R > m)
update(o, L, R, rson);
pushup(rt);
}
void query(int l, int r, int rt) {
if (inter[rt] == 1) {
for (int i = l; i <= r; i++)
vis[i] = true;
return;
}
else if (inter[rt] == 0)
return ;
if (l == r)
return;
pushdown(rt);
int m = (l + r) >> 1;
query(lson);
query(rson);
pushup(rt);
}
int main() {
build(0, N, 1);
char ord[2], le, ri;
int l, r;
while (scanf("%s %c%d,%d%c", ord, &le, &l, &r, &ri) != EOF) {
getchar();
//区间倍增
l <<= 1;
r <<= 1;
if (le == ‘(‘)
l++;
if (ri == ‘)‘)
r--;
//区间不合法
if (l > r) {
if (ord[0] == ‘I‘ || ord[0] == ‘C‘) {
build(0, N, 1);
}
continue;
}
//更新
if (ord[0] == ‘U‘) {
update(1, l, r, 0, N, 1);
}
else if (ord[0] == ‘D‘) {
update(0, l, r, 0, N, 1);
}
else if (ord[0] == ‘S‘) {
update(2, l, r, 0, N, 1);
}
else if (ord[0] == ‘C‘) {
update(0, 0, l - 1, 0, N, 1);
update(0, r + 1, N, 0, N, 1);
update(2, l, r, 0, N, 1);
}
else if (ord[0] == ‘I‘) {
update(0, 0, l - 1, 0, N, 1);
update(0, r + 1, N, 0, N, 1);
}
}
memset(vis, false, sizeof(vis));
query(0, N, 1);
int st = -1;
bool hasOut = false;//区间是否为空,即判断是否有输出
bool first = true;
for (int i = 0; i <= N; i++) {
if (st == -1 && vis[i])
st = i;
if (st != -1 && !vis[i]) {
hasOut = true;
int e = i - 1;
if (!first)
printf(" ");
else
first = false;
printf("%c%d,%d%c", st % 2 ? ‘(‘ : ‘[‘, st / 2,
(e + 1) / 2, e % 2 ? ‘)‘ : ‘]‘);
st = -1;
}
}
if (!hasOut)
puts("empty set");
else
printf("\n");
return 0;
}
疑问:在更新时,本不需增加判断叶结点返回的语句,但不加的话,就会RE。不知为何。难道更新传进去了一些非法区间?
原文地址:http://blog.csdn.net/u014357885/article/details/46511195