POJ 3225 Help with Intervals(区间更新 + 倍增区间)

题目链接:http://poj.org/problem?id=3225

题意:输入为一个操作加一个区间。初始区间为空,求所有操作完后,现在的区间。
输入例如x T。 x为操作,T为区间。
x可以为U,D,S,I,C这五种。
用S表示当前区间。
U表示 S = S ∪ T。
I 表示 S = S ∩ T。
D表示 S = S − T。
C表示 S = T − S。
S表示 S = S ⊕ T。
区间可以为如下形式(a,b),(a,b],[a,b),[a,b]。

思路:
很容易想到可以用线段树的区间查询来做。所以可以将区间用0表示不包含,1表示包含,-1表示部分包含。但是因为考虑到线段树表示的为一个点,类似于(2,3)这样的区间,查询时将会是空,因为区间端点均不包含在内。但实际查询时,这个区间是需要输出的。所以此时就需要将区间化为点,可以用倍增区间实现。

(l, r) --> [2 * l + 1, 2 * r - 1]
(l, r] --> [2 * l + 1, 2 * r]
[l, r) --> [2 * l, 2 * r - 1]
[l, r] --> [2 * l, 2 *r]

这样做的目的就是可以将(l, l + 1)这样的区间化为一个点以方便查询。

接下来对于每种操作,即化为对应的更新即可。因为倍增区间后,所有的区间已经化作为闭区间。

U [l, r]:将区间[l, r]置为1
I [l, r]:将区间[0, l - 1]和[r + 1, N]置为0
D [l, r]:将区间[l, r]置为0
C [l, r]:将区间[0, l - 1]和[r + 1, N]置为0,将区间[l, r]反转
S [l, r]:将区间[l, r]反转

最后只需要每次更新最后查询即可。然后再将区间转换为倍增前的区间。

注意
1. 区间可能不合法,即倍增后l>r。这种情况即视区间为空集即可。
2. 有2种类型更新,所以对每种更新需要分别开一个lazy数组。

代码如下

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iomanip>
#include <time.h>
using namespace std;

#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
const int N = 65535 << 1;
int inter[N << 2];
int cover[N << 2];//赋值lazy
int XX[N << 2];//异或lazy
bool vis[N << 2];

void pushup(int rt) {
    if (inter[rt << 1] == inter[rt << 1 | 1])
        inter[rt] = inter[rt << 1];
    else
        inter[rt] = -1;
}

void pushdown(int rt) {
    if (cover[rt] != -1) {
        XX[rt << 1] = XX[rt << 1 | 1] = 0;//如果赋值,本需要反转的则不需反转
        cover[rt << 1] = cover[rt << 1 | 1] = cover[rt];
        inter[rt << 1] = inter[rt << 1 | 1] = cover[rt];
        cover[rt] = -1;
    }
    if (XX[rt]) {
        if (cover[rt << 1] != -1)//若需赋值,则反转赋值的。
            cover[rt << 1] = 1 - cover[rt << 1];
        else//否则记录反转lazy
            XX[rt << 1] = 1 - XX[rt << 1];
        if (inter[rt << 1] != -1)
                inter[rt << 1] = 1 - inter[rt << 1];

        if (cover[rt << 1 | 1] != -1)
            cover[rt << 1 | 1] = 1 - cover[rt << 1 | 1];
        else
            XX[rt << 1 | 1] = 1 - XX[rt << 1 | 1];
        if (inter[rt << 1 | 1] != -1)
            inter[rt << 1 | 1] = 1 - inter[rt << 1 | 1];
        XX[rt] = 0;
    }
}

void build(int l, int r, int rt) {
    XX[rt] = 0;
    cover[rt] = -1;
    inter[rt] = 0;
    if (l == r)
        return ;
    int m = (l + r) >> 1;
    build(lson);
    build(rson);
}

void update(int o, int L, int R, int l, int r, int rt) {
    if (L <= l && r <= R) {
        if (o == 2) {
            if (cover[rt] != -1)
                cover[rt] = 1 - cover[rt];
            else
                XX[rt] = 1 - XX[rt];
            if (inter[rt] != -1)
                inter[rt] = 1 - inter[rt];
        }
        else {
            XX[rt] = 0;
            cover[rt] = o;
            inter[rt] = o;
        }
        return ;
    }
    if (l == r)
        return;
    pushdown(rt);
    int m = (l + r) >> 1;
    if (L <= m)
        update(o, L, R, lson);
    if (R > m)
        update(o, L, R, rson);
    pushup(rt);
}

void query(int l, int r, int rt) {
    if (inter[rt] == 1) {
        for (int i = l; i <= r; i++)
            vis[i] = true;
        return;
    }
    else if (inter[rt] == 0)
        return ;
    if (l == r)
        return;
    pushdown(rt);
    int m = (l + r) >> 1;
    query(lson);
    query(rson);
    pushup(rt);
}


int main() {

    build(0, N, 1);
    char ord[2], le, ri;
    int l, r;
    while (scanf("%s %c%d,%d%c", ord, &le, &l, &r, &ri) != EOF) {
        getchar();
        //区间倍增
        l <<= 1;
        r <<= 1;
        if (le == '(')
            l++;
        if (ri == ')')
            r--;
        //区间不合法
        if (l > r) {
            if (ord[0] == 'I' || ord[0] == 'C') {
                build(0, N, 1);
            }
            continue;
        }
        //更新
        if (ord[0] == 'U') {
            update(1, l, r, 0, N, 1);
        }
        else if (ord[0] == 'D') {
            update(0, l, r, 0, N, 1);
        }
        else if (ord[0] == 'S') {
            update(2, l, r, 0, N, 1);
        }
        else if (ord[0] == 'C') {
            update(0, 0, l - 1, 0, N, 1);
            update(0, r + 1, N, 0, N, 1);
            update(2, l, r, 0, N, 1);
        }
        else if (ord[0] == 'I') {
            update(0, 0, l - 1, 0, N, 1);
            update(0, r + 1, N, 0, N, 1);
        }
    }
    memset(vis, false, sizeof(vis));
        query(0, N, 1);

    int st = -1;
    bool hasOut = false;//区间是否为空,即判断是否有输出
    bool first = true;
    for (int i = 0; i <= N; i++) {
        if (st == -1 && vis[i])
            st = i;
        if (st != -1 && !vis[i]) {
            hasOut = true;
            int e = i - 1;
            if (!first)
                printf(" ");
            else
                first = false;
                printf("%c%d,%d%c", st % 2 ? '(' : '[', st / 2,
                            (e + 1) / 2, e % 2 ? ')' : ']');
            st = -1;
        }
    }
    if (!hasOut)
        puts("empty set");
    else
        printf("\n");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值