HDU 3397 Sequence operation(线段树区间合并)

题意:

0 a b将区间[a,b]所有数全部变成0
1 a b将区间[a,b]所有数全部变成1
2 a b将区间[a,b]中所有数0 1互换,0变1,1变0
3 a b输出区间[a,b]中1的个数
4 a b输出区间[a,b]中最长连续1的个数

解析:

涉及到线段树的多种操作。
mxL[0]表示左边最长连续0的个数
mxR[0]表示右边最长连续0的个数
mx[0]表示当前区间最长连续0的个数
mxL[1],mxR[1],mx[1]同上
为什么要定义mx[0],和mx[1]这些标记,是为了在区间翻转的时候得到互相的值。

查询区间最长连续1个数的过程中;
maxl=[l,m]1
maxl=[m+1,r]1
maxm=min(ml+1,rs)+min(rm,ls)
结果应该是这三个中的最大值,即 max(maxl,maxr,maxm)

注意:

有个地方需要注意下,就是懒惰传递的先后问题。
我一直WA在这里,后看别人解释,如果覆盖的话,那么异或已经没有用了,把异或标记为0即可。

还有一个问题,怎么确定是先覆盖再异或,还是先异或再覆盖。
这两个的结果可能是不同的,所以必须确定,是怎么的一种传递方法?

传递的时候,如果是覆盖传递,那么子节点的异或失效。如果是异或传递,无影响。(因为存在异或,说明之前不会有覆盖经过这里)

my code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define ls (o<<1)
#define rs (o<<1|1)
#define lson ls, L, M
#define rson rs, M+1, R
using namespace std;
const int N = 1e5 + 10;

int n, m;
int mxL[N<<2][2], mxR[N<<2][2], mx[N<<2][2];
int sum[N<<2];
int XOR[N<<2], cov[N<<2];

inline int max(int a, int b, int c) { return max(a, max(b, c)); }

void setXOR(int o, int L, int R) {
    XOR[o] ^= 1;
    swap(mxL[o][0], mxL[o][1]);
    swap(mxR[o][0], mxR[o][1]);
    swap(mx[o][0], mx[o][1]);
    sum[o] = (R - L + 1) - sum[o];
}

void setCov(int o, int L, int R) {
    int cur = cov[o], tmp = cov[o]^1;
    int len = R - L + 1;
    mxL[o][cur] = mxR[o][cur] = mx[o][cur] = len;
    mxL[o][tmp] = mxR[o][tmp] = mx[o][tmp] = 0;
    sum[o] = cur * len;
}

void pushDown(int o, int L, int R) {
    int M = (L + R)/2;
    if(cov[o] != -1) {
        cov[ls] = cov[rs] = cov[o];
        XOR[ls] = XOR[rs] = false;
        setCov(lson);
        setCov(rson);
        cov[o] = -1;
    }
    if(XOR[o]) {
        setXOR(lson);
        setXOR(rson);
        XOR[o] = false;
    }
}

void merge(int o, int L, int R, int x) {
    int M = (L + R)/2;
    mxL[o][x] = mxL[ls][x]; mxR[o][x] = mxR[rs][x];

    int len1 = M - L + 1, len2 = R - M;
    if(mxL[ls][x] == len1) mxL[o][x] += mxL[rs][x];
    if(mxR[rs][x] == len2) mxR[o][x] += mxR[ls][x];

    int midlen = mxR[ls][x] + mxL[rs][x];
    mx[o][x] = max(mx[ls][x], mx[rs][x], midlen);
}

void pushUp(int o, int L, int R) {
    merge(o, L, R, 1);
    merge(o, L, R, 0);
    sum[o] = sum[ls] + sum[rs];
}

void modify(int o, int L, int R, int ql, int qr, int op) {
    if(ql <= L && R <= qr) {
        if(op < 2) {
            cov[o] = op;
            XOR[o] = false;
            setCov(o, L, R);
        }else {
            setXOR(o, L, R);
        }
        return ;
    }
    pushDown(o, L, R);
    int M = (L + R)/2;
    if(ql <= M) modify(lson, ql, qr, op);
    if(qr > M) modify(rson, ql, qr, op);
    pushUp(o, L, R);
}

int queryMax(int o, int L, int R, int ql, int qr) {
    if(ql <= L && R <= qr) return mx[o][1];
    int M = (L + R)/2, ret = 0;
    pushDown(o, L, R);
    if(qr <= M) ret = queryMax(lson, ql, qr);
    else if(ql > M) ret = queryMax(rson, ql, qr);
    else {
        int len1 = queryMax(lson, ql, M), len2 = queryMax(rson, M+1, qr);  
        int len0 = min(mxR[ls][1], (M - ql + 1)) + min(mxL[rs][1], (qr - M));
        ret = max(len0, len1, len2);
    }
    pushUp(o, L, R);
    return ret;
}

int querySum(int o, int L, int R, int ql, int qr) {
    if(ql <= L && R <= qr) return sum[o];
    int M = (L + R)/2, ret = 0;
    pushDown(o, L, R);
    if(ql <= M) ret += querySum(lson, ql, qr);
    if(qr > M) ret += querySum(rson, ql, qr);
    pushUp(o, L, R);
    return ret;
}

void build(int o, int L, int R) {
    cov[o] = -1, XOR[o] = false;
    if(L == R) {
        int val;
        scanf("%d", &val);
        cov[o] = val;
        setCov(o, L, R);
        return ;
    }
    int M = (L + R)/2;
    build(lson);
    build(rson);
    pushUp(o, L, R);
}

int main() {
    int op, a, b;

    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &m);
        build(1, 0, n-1);
        while(m--) {
            scanf("%d%d%d", &op, &a, &b);
            if(op <= 2) {
                modify(1, 0, n-1, a, b, op);
            }else if(op == 3) {
                printf("%d\n", querySum(1, 0, n-1, a, b));
            }else {
                printf("%d\n", queryMax(1, 0, n-1, a, b));
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值