poj-3225

13 篇文章 0 订阅
// 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是否为偶数来决定左右区间的开闭(也是离散化的规则),输出即可。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值