BZOJ3226【SDOI2008】校门外的区间 <线段树>

Problem

【SDOI2008】校门外的区间

Time  Limit:  10  Sec\mathrm{Time\;Limit:\;10\;Sec}
Memory  Limit:  128  MB\mathrm{Memory\;Limit:\;128\;MB}

Description

受校门外的树这道经典问题的启发,AA君根据基本的离散数学的知识,抽象出55种运算维护集合SS(SS初始为空)并最终输出SS。现在,请你完成这道校门外的树之难度增强版——校门外的区间。
55种运算如下:

编号 表示格式 数学表示
11 U    TU\;\;T STS\cup T
22 I    TI\;\;T STS\cap T
33 D    TD\;\;T STS-T
44 C    TC\;\;T S+TS+T
55 S    TS\;\;T STS\otimes T

基本集合运算如下:

运算 结果
ABA\cup B {xxAxB}\lbrace x\mid x\in A\mid\mid x\in B\rbrace
ABA\cap B {xxA    &&    xB}\lbrace x\mid x\in A\;\;\&\&\;\;x\in B\rbrace
ABA-B {xxA    &&    xB}\lbrace x\mid x\in A\;\;\&\&\;\;x\notin B\rbrace
ABA\otimes B (AB)(BA)(A-B)\cup(B-A)

Input

输入共MM行。
每行的格式为XX TT,用一个空格隔开,XX表示运算的种类,TT为一个区间(区间用(a,b),(a,b],[a,b),[a,b](a,b), (a,b], [a,b), [a,b]表示)。

Output

共一行,即集合SS,每个区间后面带一个空格。若SS为空则输出emptyempty setset

Sample Input

1
2
3
4
5
U [1,5]
D [3,3]
S [2,4]
C (1,5)
I (2,3]

Sample Output

1
(2,3) 

HINT

对于 100%100\% 的数据,0ab655350\le a\le b\le 655351M700001\le M\le 70000

标签:线段树

Solution

假设只有闭区间,对于每个数,标记其为11还是00
四种运算对应如下(modifymodify为区间修改,reversereverse为区间取反):

  • UU a,bmodify(a,b,1)a,b\Rightarrow modify(a, b, 1)
  • DD a,bmodify(a,b,0)a,b\Rightarrow modify(a, b, 0)
  • SS a,breverse(a,b)a,b\Rightarrow reverse(a, b)
  • CC a,bmodify(0,a1,0),modify(b+1,n,0),reverse(a,b)a,b\Rightarrow modify(0, a-1, 0), modify(b+1, n, 0), reverse(a, b)
  • II a,bmodify(0,a1,0),modify(b+1,n,0)a,b\Rightarrow modify(0, a-1, 0), modify(b+1, n, 0)

再考虑加入开区间,开区间看作对应.5.5的闭区间。即将(4,7)(4, 7)看作[4.5,6.5][4.5, 6.5]
为了存.5.5的小数,我们将所有区间均乘22,即(4,7)(4, 7)变为[9,13][9, 13]
又考虑到有00的区间,因而对于所有区间两端点再加22,即(4,7)(4,7)变为[11,15][11, 15]
最后用线段树维护即可。
对于输出,O(nlog(n))O(n\log(n))扫一遍,找出每个数是11还是00,然后合并区间,双指针跑。
本题细节较多,写的时候得小心。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>
#include <cstdio>
#define n (65536*2+1)
using namespace std;
struct node {int val, tag, rev;} tr[n*4+500];
void build(int v, int s, int t) {
tr[v].tag = -1; if (s == t) return; int mid = s+t>>1;
build(v<<1, s, mid), build(v<<1|1, mid+1, t);
}
void downtag(int v, int s, int t) {
if (s == t) {if (~tr[v].tag) tr[v].val = tr[v].tag; tr[v].val ^= tr[v].rev, tr[v].tag = -1, tr[v].rev = 0; return;}
if (~tr[v].tag) tr[v<<1].tag = tr[v<<1|1].tag = tr[v].tag, tr[v<<1].rev = tr[v<<1|1].rev = 0;
tr[v<<1].rev ^= tr[v].rev, tr[v<<1|1].rev ^= tr[v].rev; tr[v].tag = -1, tr[v].rev = 0;
}
int query(int v, int s, int t, int p) {
downtag(v, s, t); if (s == t) return tr[v].val; int mid = s+t>>1;
return p <= mid ? query(v<<1, s, mid, p) : query(v<<1|1, mid+1, t, p);
}
void modify(int v, int s, int t, int l, int r, int x) {
if (s > t) return; downtag(v, s, t);
if (s >= l && t <= r) {tr[v].tag = x; return;}
int mid = s+t>>1;
if (l <= mid) modify(v<<1, s, mid, l, r, x);
if (r >= mid+1) modify(v<<1|1, mid+1, t, l, r, x);
}
void reverse(int v, int s, int t, int l, int r) {
if (s > t) return; downtag(v, s, t);
if (s >= l && t <= r) {tr[v].rev ^= 1; return;}
int mid = s+t>>1;
if (l <= mid) reverse(v<<1, s, mid, l, r);
if (r >= mid+1) reverse(v<<1|1, mid+1, t, l, r);
}
int main() {
char opt, lbr, rbr; int l, r; build(1, 1, n);
while (~scanf("%c %c%d,%d%c\n", &opt, &lbr, &l, &r, &rbr)) {
l <<= 1, r <<= 1, l = l+(lbr == '(')+2, r = r-(rbr == ')')+2;
if (opt == 'U') modify(1, 1, n, l, r, 1);
if (opt == 'I') modify(1, 1, n, 1, l-1, 0), modify(1, 1, n, r+1, n, 0);
if (opt == 'D') modify(1, 1, n, l, r, 0);
if (opt == 'C') modify(1, 1, n, 1, l-1, 0), modify(1, 1, n, r+1, n, 0), reverse(1, 1, n, l, r);
if (opt == 'S') reverse(1, 1, n, l, r);
}
int st = -1, en = -1; bool flag = false;
for (int i = 1; i <= n; i++) {
if (query(1, 1, n, i)) {
if (st == -1) st = i;
en = i;
} else {
if (~st) {
if (flag) printf(" ");
else flag = true;
printf("%c", (st%2 == 1) ? '(' : '[');
printf("%d,%d", st/2-1, (en+1)/2-1);
printf("%c", (en%2 == 1) ? ')' : ']');
}
st = en = -1;
}
}
if (!flag) printf("empty set");
return 0;
}