P2572 [SCOI2010]序列操作(线段树毒瘤题)

在做着题之前, 可以先看一看这一题的简化版:STEP
反正就一个套路
题目传送门:序列操作

给定长度为 n n n 01 01 01序列, 有五种操作

  1. 将下标 [ l , r ] [l,r] [l,r]内的数全改为 0 0 0
  2. 将下标 [ l , r ] [l,r] [l,r]内的数全改为 1 1 1
  3. 将下标 [ l , r ] [l,r] [l,r]内的数 0 0 0改为 1 1 1, 1 1 1 改为 0 0 0
  4. ∑ i = l r a [ i ] \displaystyle\sum_{i=l}^{r}a[i] i=lra[i]
  5. 求下标 [ l , r ] [l,r] [l,r]内最多有多少个连续的 1 1 1

太毒瘤了, 写得头皮发麻;
主要是细节太多了. 见代码
关于为何 p u s h d o w n pushdown pushdown要放在前面, 因为若区间要先进行赋值再进行取反操作, 放后面的话赋值会覆盖掉取反.

// -----[今天不努力, 明天变垃圾]-----
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
#define x first
#define y second
const int MAX_N = 2e5 + 5;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-6;
//
//
int n, m;
int a[MAX_N];
// sum维护区间总和, lazy维护区间赋值, rev维护区间翻转
// mx[0], lmx[0], rmx[0], 分别维护区间连续0的最大个数, 包括左端点的连续0的最大个数, 包括右端点的连续0的最大个数
// 1 同理
struct SegMentTree {
    int l, r;
    int sum, lazy, rev;
    int mx[2], lmx[2], rmx[2];
} tree[MAX_N * 4];
#define l(x) tree[x].l
#define r(x) tree[x].r
#define sum(x) tree[x].sum
#define lazy(x) tree[x].lazy
#define rev(x) tree[x].rev
inline void push_up(int x) {
    int llx = x * 2;
    int rrx = x * 2 + 1;
    sum(x) = sum(llx) + sum(rrx);
    for(int i = 0; i <= 1; ++ i) {
        tree[x].lmx[i] = tree[llx].lmx[i];
        if(i == 1 && sum(llx) == r(llx) - l(llx) + 1) { // 左区间全为1
            tree[x].lmx[i] += tree[rrx].lmx[i];
        } else if(i == 0 && sum(llx) == 0) { // 左区间全为0
            tree[x].lmx[i] += tree[rrx].lmx[i];
        }
        tree[x].rmx[i] = tree[rrx].rmx[i];
        if(i == 1 && sum(rrx) == r(rrx) - l(rrx) + 1) { // 右区间全为1
            tree[x].rmx[i] += tree[llx].rmx[i];
        } else if(i == 0 && sum(rrx) == 0) { // 右区间全为0
            tree[x].rmx[i] += tree[llx].rmx[i];
        }
        tree[x].mx[i] = tree[llx].rmx[i] + tree[rrx].lmx[i]; // 左右合并
        tree[x].mx[i] = max(tree[x].mx[i], max(tree[llx].mx[i], tree[rrx].mx[i])); // 继承左右区间
    }
}
inline void push_down(int x) {
    int llx = x * 2;
    int rrx = x * 2 + 1;
    if(lazy(x) != -1) { // 有区间赋值时优先进行区间赋值 , 然后清除翻转标记
        int k = lazy(x); // 延迟标记既代表了区间赋值, 也代表了整个区间的颜色
        sum(llx) = k * (r(llx) - l(llx) + 1);
        sum(rrx) = k * (r(rrx) - l(rrx) + 1);
        lazy(llx) = lazy(rrx) = k;
        rev(llx) = rev(rrx) = 0;
        tree[llx].mx[k] = tree[llx].lmx[k] = tree[llx].rmx[k] = r(llx) - l(llx) + 1; // 区间内全为该种颜色
        tree[llx].mx[k ^ 1] = tree[llx].lmx[k ^ 1] = tree[llx].rmx[k ^ 1] = 0; // 另一种颜色就消失了
        tree[rrx].mx[k] = tree[rrx].lmx[k] = tree[rrx].rmx[k] = r(rrx) - l(rrx) + 1; // 同理
        tree[rrx].mx[k ^ 1] = tree[rrx].lmx[k ^ 1] = tree[rrx].rmx[k ^ 1] = 0;
        lazy(x) = -1;
        rev(x) = 0; // 清除翻转标记
    }
    if(rev(x)) {
        sum(llx) = r(llx) - l(llx) + 1 - sum(llx);
        sum(rrx) = r(rrx) - l(rrx) + 1 - sum(rrx);
        if(lazy(llx) != -1) lazy(llx) ^= 1;
        else rev(llx) ^= 1;
        if(lazy(rrx) != -1) lazy(rrx) ^= 1;
        else rev(rrx) ^= 1;
        // 翻转
        swap(tree[llx].mx[0], tree[llx].mx[1]);
        swap(tree[llx].lmx[0], tree[llx].lmx[1]);
        swap(tree[llx].rmx[0], tree[llx].rmx[1]);
        // 同理
        swap(tree[rrx].mx[0], tree[rrx].mx[1]);
        swap(tree[rrx].lmx[0], tree[rrx].lmx[1]);
        swap(tree[rrx].rmx[0], tree[rrx].rmx[1]);
        rev(x) = 0;
    }
}
void build(int x, int l, int r) {
    int llx = x * 2;
    int rrx = x * 2 + 1;
    l(x) = l; r(x) = r;
    lazy(x) = -1;
    if(l == r) {
        sum(x) = a[l];
        tree[x].mx[a[l]] = tree[x].lmx[a[l]] = tree[x].rmx[a[l]] = 1;
        return;
    }
    int mid = (l + r) / 2;
    build(llx, l, mid);
    build(rrx, mid + 1, r);
    push_up(x);
}
void update(int x, int l, int r, int val) {
    int llx = x * 2;
    int rrx = x * 2 + 1;
    int mid = (r(x) + l(x)) / 2;
    push_down(x);
    if(l(x) == l && r(x) == r) {
        if(val == 0 || val == 1) {
            sum(x) = (r(x) - l(x) + 1) * val;
            lazy(x) = val;
            tree[x].mx[val] = tree[x].lmx[val] = tree[x].rmx[val] = r(x) - l(x) + 1;
            tree[x].mx[val ^ 1] = tree[x].lmx[val ^ 1] = tree[x].rmx[val ^ 1] = 0;
        } else if(val == 2) {
            sum(x) = r(x) - l(x) + 1 - sum(x);
            rev(x) ^= 1;
            // 翻转
            swap(tree[x].mx[0], tree[x].mx[1]);
            swap(tree[x].lmx[0], tree[x].lmx[1]);
            swap(tree[x].rmx[0], tree[x].rmx[1]);
        }
        return;
    }
    if(l >= mid + 1) update(rrx, l, r, val);
    else if(r <= mid) update(llx, l, r, val);
    else { update(llx, l, mid, val); update(rrx, mid + 1, r, val);}
    push_up(x);
}
//bool operator < (const SegMentTree &a, const SegMentTree &b) {
//    return a.mx[1] < b.mx[1];
//}
int query_sum(int x, int l, int r) {
    int llx = x * 2;
    int rrx = x * 2 + 1;
    int mid = (r(x) + l(x)) / 2;
    push_down(x);
    if(l(x) == l && r(x) == r) {
        return sum(x);
    }
    if(l >= mid + 1) return query_sum(rrx, l, r);
    else if(r <= mid) return query_sum(llx, l, r);
    else return query_sum(llx, l, mid) + query_sum(rrx, mid + 1, r);
}
SegMentTree query_max(int x, int l, int r) {
    int llx = x * 2;
    int rrx = x * 2 + 1;
    int mid = (r(x) + l(x)) / 2;
    push_down(x);
    if(l(x) == l && r(x) == r) return tree[x];
    if(l >= mid + 1) {
        return query_max(rrx, l, r);
    } else if(r <= mid) {
        return query_max(llx, l, r);
    } else {
        SegMentTree res;
        SegMentTree L = query_max(llx, l, mid), R = query_max(rrx, mid + 1, r);
        res.sum = L.sum + R.sum;
        for(int i = 0; i <= 1; ++ i) {
            res.lmx[i] = L.lmx[i];
            if(i == 1 && L.sum == L.r - L.l + 1) { // 左区间全为1
                res.lmx[i] += R.lmx[i];
            } else if(i == 0 && L.sum == 0) { // 左区间全为0
                res.lmx[i] += R.lmx[i];
            }
            res.rmx[i] = R.rmx[i];
            if(i == 1 && R.sum == R.r - R.l + 1) { // 右区间全为1
                res.rmx[i] += L.rmx[i];
            } else if(i == 0 && R.sum == 0) { // 右区间全为0
                res.rmx[i] += L.rmx[i];
            }
            res.mx[i] = L.rmx[i] + R.lmx[i]; // 左右合并
            res.mx[i] = max(res.mx[i], max(L.mx[i], R.mx[i])); // 继承左右区间
        }
        return res;
    }
}
void solve() {
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; ++ i) scanf("%d", &a[i]);
    build(1, 1, n);
    while(m -- ) {
        int op, l, r;
        scanf("%d %d %d", &op, &l, &r);
        ++ l; ++ r;
        if(op == 0 || op == 1 || op == 2) {
            update(1, l, r, op);
        } else if(op == 3) {
            printf("%d\n", query_sum(1, l, r));
        } else {
            printf("%d\n", query_max(1, l, r).mx[1]);
        }
    }
}
signed main() {
//    ios::sync_with_stdio(false);
//    cin.tie(nullptr);
//    cout.tie(nullptr);
    int ZTY = 1;
    //cin >> ZTY;
    while(ZTY -- ) {
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值