poj-3225

来源:互联网 发布:淘宝联盟加入购物车 编辑:程序博客网 时间:2024/06/01 17:30
// 4808K    1016MS  C++#include <cstdio>#include <cstring>using namespace std;// Every node split to 2 node !!!!!!!!!!!!!!!!!!!!!!!!!!, NB trick.// e,g: 1,2 -> 12,23const 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是否为偶数来决定左右区间的开闭(也是离散化的规则),输出即可。 

0 0
原创粉丝点击