poj 3225 Help with Intervals(线段树进阶,处理区间,拆点)

本文详细介绍了如何使用线段树进行区间交并补运算,包括标记与更新操作,以及如何通过线段树实现对称差等集合操作。通过实例演示了线段树在解决区间问题时的高效性和灵活性。

题意:
求区间的交,并,补,对称差。。
思路:
为了表示开闭区间,用偶数表示闭区间端点,奇数表示开区间的。
[2,3) -> 4, 5
(3, 7) -> 7, 13
集合操作可以分解为两种,set和xor。。
先实现这两种操作,再讨论情况。。

const int Maxn = 65550*2;

#define lson(x) ((x)<<1)
#define rson(x) (((x)<<1)|1)
struct node {
    int cover, x;
};
node a[Maxn*4+5];

void xor_helper(int o) {
    if (a[o].cover == -1) {
        a[o].x ^= 1;
    } else {
        a[o].cover ^= 1;
    }
}

// 两种标记不会同时存在
void push_down(int o) {
    int lc = lson(o), rc = rson(o);
    if (a[o].cover != -1) {
        a[lc].cover = a[rc].cover = a[o].cover;
        a[lc].x = a[rc].x = 0;
        a[o].cover = -1;
    }
    if (a[o].x) {
        xor_helper(lc);xor_helper(rc);
        a[o].x = 0;
    }
}

void seg_update_cover(int L, int R, int o, int qL, int qR, int v) {
    if (qL <= L && R <= qR) {
        a[o].cover = v;
        a[o].x = 0;
        return;
    }

    push_down(o);
    int lc = lson(o), rc = rson(o), mid = (L+R)>>1;
    if (qL <= mid) seg_update_cover(L, mid, lc, qL, qR, v);
    if (qR > mid) seg_update_cover(mid+1, R, rc, qL, qR, v);
}

void seg_update_xor(int L, int R, int o, int qL, int qR) {
    if (qL <= L && R <= qR) {
        xor_helper(o);return;
    }

    push_down(o);
    int lc = lson(o), rc = rson(o), mid = (L+R)>>1;
    if (qL <= mid) seg_update_xor(L, mid, lc, qL, qR);
    if (qR > mid) seg_update_xor(mid+1, R, rc, qL, qR);
}

// query and mark
int mark[Maxn+5];
void seg_query(int L, int R, int o) {
   if (a[o].cover == 1) {rep(i, L-1, R-1) mark[i] = 1;return;}
   if (!a[o].cover) return;
   push_down(o);
    int lc = lson(o), rc = rson(o), mid = (L+R)>>1;
    seg_query(L, mid, lc);seg_query(mid+1, R, rc);
}

void seg_build(int L, int R, int o) {
     a[o] = (node) {0, 0};
     if (L < R) {
         int lc = lson(o), rc = rson(o), mid = (L+R)>>1;
         seg_build(L, mid, lc);seg_build(mid+1, R, rc);
     }
}

// debug
void seg_print(int L, int R, int o, int num) {
    if (a[o].cover == 1) {printf("(%d %d)\n", L-1, R-1);return;}
    if (a[o].cover == 0)return;
    push_down(o);
    int lc = lson(o), rc = rson(o), mid = (L+R)>>1;
    seg_print(L, mid, lc, num+1);seg_print(mid+1, R, rc, num+1);
}

void ask() {
    int n = Maxn;
    memset(mark, 0, sizeof(mark));
    seg_query(1, n, 1);
    vector<pair<int, int> > ans;
    int s = -1, e = -1;
    for(int i=0;i<Maxn;++i) {
        if (mark[i]) {
            if (s == -1) s = i;
            e = i;
        } else {
            if (s != -1) {
                ans.push_back(make_pair(s, e));
                s = -1;
            }
        }
    }
    if (ans.empty()) {printf("empty set\n");return;}
    for (int i=0;i<ans.size();++i) {
        int a = ans[i].first, b = ans[i].second;
        char lhs = '[', rhs = ']';
        if (ans[i].first & 1) {
            lhs = '(';
            --a;
        }
        if (ans[i].second & 1) {
            rhs = ')';
            ++b;
        }
        printf("%c%d,%d%c", lhs, a/2, b/2, rhs);
        printf("%c", i == ans.size()-1 ? '\n':' ');
    }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.in", "r", stdin);
#endif // ONLINE_JUDGE
    char a, b, c;int x, y;
    int n = Maxn;
    seg_build(1, n, 1);
    while (scanf("%c %c%d,%d%c\n", &a, &b, &x, &y, &c) != EOF) {
        x *= 2;y *= 2;
        if (b == '(') ++x;if (c == ')') --y;

        x += 1;y += 1; // 因为最小为0,所以对应到线段树上的区间时+1
        //printf("%c %d %d\n", a, x-1, y-1);
        if (x > y) continue;
        if (a == 'U') {
            seg_update_cover(1, n, 1, x, y, 1);
        } else if (a == 'I') {
            if (x - 1 >= 1)
                seg_update_cover(1, n, 1, 1, x-1, 0);
            seg_update_cover(1, n, 1, y+1, Maxn, 0);
        } else if (a == 'D') {
            seg_update_cover(1, n, 1, x, y, 0);
        } else if (a == 'C') {
            if (x - 1 >= 1)
                seg_update_cover(1, n, 1, 1, x-1, 0);
            seg_update_cover(1, n, 1, y+1, Maxn, 0);
            seg_update_xor(1, n, 1, x, y);
        } else if (a == 'S') {
            seg_update_xor(1, n, 1, x, y);
        }
        //seg_print(1, n, 1, 0);
        //ask();
    }
    ask();

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值