// 4808K 1016MS C++
#include <cstdio>
#include <cstring>
using namespace std;
// Every node split to 2 node !!!!!!!!!!!!!!!!!!!!!!!!!!, NB trick.
// e,g: 1,2 -> 12,23
const int MAX = 131172;
const int POS_MAX = MAX<<2;
struct TreeNode {
int left;
int right;
int color; //0: all no choosed 1: all choosed, -1: combined
char rev;
};
typedef struct TreeNode TreeNode;
TreeNode tree[MAX<<2];
void buildTree(int pos, int begin, int end) {
TreeNode & curNode = tree[pos];
curNode.left = begin;
curNode.right = end;
curNode.color = 0;
curNode.rev = 0;
if (begin == end) {
return;
} else {
int mid = (begin + end)>>1;
buildTree(pos<<1, begin, mid);
buildTree(pos<<1|1, mid + 1, end);
}
}
void pushDown(int pos) {
TreeNode & curNode = tree[pos];
int left = curNode.left;
int right = curNode.right;
int color = curNode.color;
char rev = curNode.rev;
// printf("pushDown %d %d %d %d\n", left, right, color, rev);
if (left < right) {
TreeNode & leftNode = tree[pos<<1];
TreeNode & rightNode = tree[pos<<1|1];
if (color != -1) {
leftNode.color = color;
rightNode.color = color;
curNode.color = -1;
curNode.rev = 0;
}
if (curNode.rev) {
// printf("pushDownLeft1 %d %d %d %d\n", leftNode.left, leftNode.right,
// leftNode.color, leftNode.rev);
if (leftNode.color != -1) {
leftNode.color = (leftNode.color ? 0: 1);
} else {
leftNode.rev = leftNode.rev ? 0: 1;
}
// printf("pushDownLeft %d %d %d %d\n", leftNode.left, leftNode.right,
// leftNode.color, leftNode.rev);
// printf("pushDownRight1 %d %d %d %d\n", rightNode.left, rightNode.right,
// rightNode.color, rightNode.rev);
if (rightNode.color != -1) {
rightNode.color = (rightNode.color ? 0: 1);
} else {
rightNode.rev = rightNode.rev ? 0: 1;
}
// printf("pushDownRight %d %d %d %d\n", rightNode.left, rightNode.right,
// rightNode.color, rightNode.rev);
curNode.rev = 0;
}
}
}
void update(int pos, int rangeLeft, int rangeRight, int choosed) {
if (rangeLeft > rangeRight) {
return;
}
TreeNode & curNode = tree[pos];
int left = curNode.left;
int right = curNode.right;
int color = curNode.color;
if (rangeLeft <= left && rangeRight >= right) {
curNode.color = choosed;
curNode.rev = 0;
return;
// if (OP == 'B' && choosed == 0) {
// curNode.color = 0;
// curNode.rev = 0;
// // printf("update %d %d %d %d %d\n", rangeLeft, rangeRight, left, right, curNode.color);
// return;
// } else if (OP == 'Y' && choosed == 1) {
// curNode.color = 1;
// curNode.rev = 0;
// // printf("update %d %d %d %d %d\n", rangeLeft, rangeRight, left, right, curNode.color);
// return;
// } else if (color != -1) {
// if (OP == 'B') {
// curNode.color = choosed & color;
// } else if (OP == 'Y') {
// curNode.color = choosed | color;
// }
// curNode.rev = 0;
// // printf("update %d %d %d %d %d\n", rangeLeft, rangeRight, left, right, curNode.color);
// return;
// }
}
pushDown(pos);
int mid = (left + right)>>1;
if (rangeRight <= mid) {
// printf("1 %d\n", mid);
// update(pos<<1, rangeLeft, rangeRight, choosed, OP);
update(pos<<1, rangeLeft, rangeRight, choosed);
} else if (rangeRight > mid && rangeLeft <= mid) {
// printf("2 %d\n", mid);
// update(pos<<1, rangeLeft, mid, choosed, OP);
update(pos<<1, rangeLeft, mid, choosed);
// update(pos<<1|1, mid + 1, rangeRight, choosed, OP);
update(pos<<1|1, mid + 1, rangeRight, choosed);
} else if (rangeLeft > mid) {
// printf("3 %d\n", mid);
// update(pos<<1|1, rangeLeft, rangeRight, choosed, OP);
update(pos<<1|1, rangeLeft, rangeRight, choosed);
}
}
void reverse(int pos, int rangeLeft, int rangeRight) {
if (rangeLeft > rangeRight) {
return;
}
TreeNode & curNode = tree[pos];
int left = curNode.left;
int right = curNode.right;
int color = curNode.color;
// printf("reverse %d %d %d %d %d\n", rangeLeft, rangeRight, left, right, color);
if (rangeLeft <= left && rangeRight >= right) {
if (curNode.color != -1) {
curNode.color = curNode.color ? 0: 1;
} else {
curNode.rev = curNode.rev ? 0: 1;
}
return;
}
pushDown(pos);
int mid = (left + right)>>1;
if (rangeRight <= mid) {
reverse(pos<<1, rangeLeft, rangeRight);
} else if (rangeRight > mid && rangeLeft <= mid) {
reverse(pos<<1, rangeLeft, mid);
reverse(pos<<1|1, mid + 1, rangeRight);
} else if (rangeLeft > mid) {
reverse(pos<<1|1, rangeLeft, rangeRight);
}
}
char hash[MAX];
void setHash(int pos) {
if (pos > POS_MAX) {
return;
}
TreeNode & curNode = tree[pos];
int left = curNode.left;
int right = curNode.right;
int color = curNode.color;
// printf("setHash1 %d %d %d\n", left, right, color);
if (color == 1) {
// printf("setHash %d %d 1\n", left, right);
for (int i = left; i<= right; i++) {
hash[i] = 1;
}
return;
} else if (color == -1) {
pushDown(pos);
setHash(pos<<1);
setHash(pos<<1|1);
}
}
void printFinalResult() {
// printf("printFinalResult\n");
setHash(1);
int begin = -1;
int num = 0;
for (int i = 0; i < MAX; i++) {
if (hash[i]) {
if (begin == -1) { // encounter a range begin
// printf("printFinalResult %d\n", i);
begin = i;
}
} else if (begin != -1) { // encouter a range end
// printf("%d %d\n", begin, i-1);
int left = (begin)/2 - 1;
int right = (i - 1 + 1)/2 - 1;
char leftBacket = begin%2 ? '(' :'[';
char rightBacket = (i-1)%2 ? ')': ']';
if (num) {
printf(" ");
}
printf("%c%d,%d%c", leftBacket, left, right, rightBacket);
begin = -1;
num++;
}
}
if (!num) {
printf("empty set\n");
} else {
printf("\n");
}
}
int main() {
char OPType ;
char leftFlag;
int left;
int right;
char rightFlag;
char str[20];
memset(hash, 0, sizeof(hash));
buildTree(1, 0, MAX);
// while(scanf("%s", str) != EOF) {
while(scanf("%s %c%d,%d%c", str, &leftFlag, &left, &right, &rightFlag) != EOF) {
OPType = str[0];
// while (scanf("%c %c%d,%d%c\n", &OPType, &leftFlag, &left, &right, &rightFlag) != EOF) {
// printf("AAAA %d %d\n", left, right);
// scanf("%c", &OPType);
// scanf("%c%d,%d%c", &leftFlag, &left, &right, &rightFlag);
// printf("%c %c%d,%d%c\n", OPType, leftFlag, left, right, rightFlag);
left = (left+1)<<1;
right = (right+1)<<1;
if (leftFlag == '(') {
left++;
}
if (rightFlag == ')') {
right--;
}
// printf("BBBB %d %d\n", left, right);
if (left > right) { // null collection, e.g: (2,2) (3,2) [3,1]
if (OPType == 'I'|| OPType == 'C') {
update(1, 0, MAX, 0);
}
continue;
}
switch(OPType) {
case 'U':
// update(1, left, right, 1, 'Y');
update(1, left, right, 1);
break;
case 'I':
// update(1, 0, left-1, 0, 'B');
update(1, 0, left-1, 0);
// update(1, right+1, MAX, 0, 'B');
update(1, right+1, MAX, 0);
break;
case 'D':
// update(1, left, right, 0, 'B');
update(1, left, right, 0);
break;
case 'C':
// update(1, 0, left-1, 0, 'B');
update(1, 0, left-1, 0);
// update(1, right+1, MAX, 0, 'B');
update(1, right+1, MAX, 0);
// printFinalResult();
reverse(1, left, right);
break;
case 'S':
reverse(1, left, right);
break;
default:
break;
}
}
printFinalResult();
}
终于AC了,巨纠结的一道题. 用了一种很trick的方法来表现出闭区间和开区间,即一个单位区间被拆分为3个单位区间段,
举个例子:
1到2这个区间可以表示为:
1<->2<->3, 然后就可以表示 1到2各种开闭区间的组合了: 1<->2表示 [1,2), 1<->2<->3 表示 [1,2], 2<->3表示 (1, 2], 只有一个2 则表示 (1,2)
这样就可以用线段树的点线段来表示开闭区间了(在线段树节点中加标记表示开闭区间是解决不了该问题的,必须用这种离散化的方法), 是个很有用的trick,将抽象的
开闭区间也用实际的数字表示了出来。
那么首先做的就是把输入的数字和区间开闭标记 都转化成 离散化以后的闭区间,
每个输入的区间 S<-> D,根据上面离散化的过程,有四种情况:
case1:左右都是闭区间,那么 离散化以后的是 [2S , 2D],
case2:左右都是开区间,那么离散化以后就是 [2S+1, 2D-1],
case3: 左边闭,右边开, 那么离散化以后就是 [2S, 2D-1],
case4:左边开,右边闭,那么离散化以后就是 [2S+1, 2D]
离散化以后,还要判断 是否区间是有效的,如果是无效区间(比如 (2,2) (2,1], (3,1))那么就认为其是空集。
因为离散化,数据的范围增大了一倍,因此在开线段树数组以及建树的时候要用原来数据上限 M (65536)的2倍 2M 来做。
在解决开闭区间的表示问题了以后,就要考虑如何模拟题目的5种集合操作了:
设本次操作的区间是 [l, r],之前的集合是S
(1表示该位置属于集合, 0表示该位置不属于集合)
U:把区间[l,r]覆盖成1 (很因为是并,因此只需把该区间所有位置置为1即可)
I:把[-∞,l)(r,∞]覆盖成0 (交集,[l, r]外边的所有位置置为0, 而[l, r]内的不需要改变,原来是1的,交以后还是1, 原来是0的,交以后还是0)
D:把区间[l,r]覆盖成0 (其实就是从现有的集合,去掉集合[l,r], 全部置0)
C:把[-∞,l)(r,∞]覆盖成0 , 且[l,r]区间0/1互换 (从[l, r]中去掉原来的集合,那么必然的 [l, r]之外的要全部置0, 而[l, r]内部的,原来不属于S的会属于新的集合,因此应该置为1, 而原来属于S的,要拿掉,因此要置为0, 所以就是将 [1, r]中的 1,0 对调)
S:[l,r]区间0/1互换 (只有不同时属于[l, r]和 S的才属于新的集合, 因此 [l, r]之外的必然属于新的集合,不需要改变, 而在 [l, r]内部, 原来属于S的, 这次就同时属于了[l, r]和 S, 因此要被拿掉,置为1, 而原来不属于S的,就满足了属于[l, r]而不属于S的条件,属于新的集合,因此置为1, 所有就是将[l, r]中的1, 0对调)
注意上面的区间[l, r]是离散化以后的区间,这样才能用闭区间来表示任何的原始开闭区间,才能进行上面的分析。
从上面的分析可以看出来,这次的线段树操作有两种:
一种是将整个区间置为0或1, update(l, r, v(0/1))
一种是将整个区间的0,1对调。 reverse(l, r)
而为了满足时间的需求,这两种操作都要用lazyTag的方式来表示:
一个color表示 当前区间的0或1, 同时也表示这个染色的操作被lazy在此区间, color = 0/1分别对应此区间全部为0/1以及被lazy的染色操作是0/1, 为-1时,表示该区间没有被lazy的染色操作,同时该区间是0/1混杂。
一个rev表示 该区间是否有被delay的 0/1反转操作, rev = 0/1分别表示该区间没有/有 被lazy的0/1反转操作。
在pushDown的时候,就要考虑这两个lazyTag了,
首先看是否有被lazy的染色操作,如果有,那么就将子区间染色,并继承color(和rev的继承逻辑不同,直接覆盖原来的color值), 同时将此区间的rev置为0(注意,染色表示的是一种对该区间颜色的确定,因此不需要考虑反转,这个在update的时候会被保证)。
如果没有被lazy的染色操作,就检查是否有被lazy的反转操作,如果有,那么分别检查其左右子区间,如果子区间的color不是-1,那么将子区间的color反转,否则,子区间结合自己的rev状态和父区间的rev来设置自己的rev(如果子区间本身就要反转,而父区间又下发了一个反转,那么综合以后就是两次反转,即没有变化,即不需要反转 rev设为 0,这一点很重要,当时没意识到,搞成了和color一样的逻辑,结果卡了很长时间。。。。 color == -1表示 子区间也是0/1混杂,因此不能在这一层做反转,只能等到下一层再看是否可以)。
这里把update 和 reverse分别实现了(其实两者完全可以放在一个函数)
步骤和以前的线段树操作一样,都是看当前进行操作区间是否被 update或reverse的区间覆盖,如果完全覆盖, 那么直接将该区间的color/rev设上返回即可,否则就pushDown,然后根据区间的交集情况来递归操作。
最后的收集整个集合的不相交区间也比较有意思,
要开一个flag数组H,大小是2M, 对应离散化以后的每个点, 然后对线段树进行query, 如果某个区间[l, r]的color==1,代表此区间存在集合内,就将H[l] 到 H[r]全部设为1, 如果 color == -1, 就pushDown, 然后递归查询。
最后再遍历H, 找到其上面连续为1的区间[l, r](这样就解决线段树一个连续的集合 被分割在几个线段树区间节点 不好统计的问题), 再将l 和 r反离散化得到真实的 l1 和 r1, 再根据l和r是否为偶数来决定左右区间的开闭(也是离散化的规则),输出即可。