NC20279 [SCOI2010]序列操作

空白菌發表於2023-05-02

題目連結

題目

題目描述

lxhgww最近收到了一個01序列,序列裡面包含了n個數,這些數要麼是0,要麼是1,現在對於這個序列有五種變換操作和詢問操作:

0 a b 把[a, b]區間內的所有數全變成0

1 a b 把[a, b]區間內的所有數全變成1

2 a b 把[a,b]區間內的所有數全部取反,也就是說把所有的0變成1,把所有的1變成0

3 a b 詢問[a, b]區間內總共有多少個1

4 a b 詢問[a, b]區間內最多有多少個連續的1

對於每一種詢問操作,lxhgww都需要給出回答,聰明的程式設計師們,你們能幫助他嗎?

輸入描述

輸入資料第一行包括2個數,n和m,分別表示序列的長度和運算元目
第二行包括n個數,表示序列的初始狀態
接下來m行,每行3個數,op, a, b,(0 ≤ op ≤ 4,0 ≤ a ≤ b)

輸出描述

對於每一個詢問操作,輸出一行,包括1個數,表示其對應的答案

示例1

輸入

10 10
0 0 0 1 1 0 1 0 1 1
1 0 2
3 0 5
2 2 2
4 0 4
0 3 6
2 3 7
4 2 8
1 0 5
0 5 6
3 3 9

輸出

5
2
6
5

備註

對於30%的資料, \(1\le n,m \le 1000\)
對於100%的資料, \(1\le n,m \le 10^5\)

題解

知識點:線段樹。

這一道題維護的資訊較多需要逐一分析。

為了方便求區間長度,還有取反的操作,我們將 \(0,1\) 的資訊都維護一下,但接下來只講 \(1\) 的部分, \(0\)\(1\) 就不講了。

首先需要維護的是 \(1\) 的數量 \(sum1\) ,以及連續 \(1\) 個數的最大值 \(max1\)

在合併時, \(sum1\) 直接加即可。 \(max1\) 不僅要取子區間的 \(max1\) , 還需要考慮左子區間從右端點開始連續的 \(1\) ,以及右子區間從左端點開始連續的 \(1\) ,兩部分拼起來的長度。因此還需要維護,區間從左端點開始連續 \(1\) 的個數 \(left1\) ,從右端點開始連續 \(1\) 的個數 \(right1\)

對於 \(left1,right1\) ,在合併時,要考慮一個特殊情況,左子區間 \(left1\) 等於區間長度(可用 \(sum0 + sum1\) 表示),那麼他可以與右子區間的 \(left1\) 相加,得到區間的 \(left1\)\(right1\) 同理。除此之外,直接繼承即可。

因此,區間資訊需要維護 \(sum0/1,max0/1,left0/1,right0/1\)

區間修改需要維護三種修改標記:全 \(0\) 、全 \(1\) 、取反。三種的區間修改都十分好實現,前兩種直接改為區間長度,取反交換 \(0/1\) 即可。另外,考慮到標記下傳需要一個標記表示沒有修改,即單位元值,以供函式特判。

因此,區間修改需要維護 \(0/1/2/3\) (無修改、全 \(0\) 、全 \(1\) 、取反)。

懶標記的修改需要分類討論:

  1. 修改為未修改,則新標記維持原狀。
  2. 修改為全 \(0\) ,則新標記為全 \(0\)
  3. 修改為全 \(1\) ,則新標記為全 \(1\)
  4. 修改為取反,則分類討論:
    1. 若原標記為未修改,則新標記為取反。
    2. 若原標記為全 \(0\) ,則新標記為全 \(1\)
    3. 若原標記為全 \(1\) ,則新標記為全 \(0\)
    4. 若原標記為取反,則新標記為未修改。

於是所有資訊就維護完了。

時間複雜度 \(O((n+m) \log n)\)

空間複雜度 \(O(n)\)

程式碼

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

struct T {
    int sum0, sum1;
    int max0, max1;
    int left0, left1;
    int right0, right1;
    static T e() {
        return {
            0,0,
            0,0,
            0,0,
            0,0
        };
    }
    friend T operator+(const T &a, const T &b) {
        return{
            a.sum0 + b.sum0,a.sum1 + b.sum1,
            max({a.max0,b.max0,a.right0 + b.left0}),max({a.max1,b.max1,a.right1 + b.left1}),
            a.left0 == a.sum0 + a.sum1 ? a.left0 + b.left0 : a.left0,a.left1 == a.sum0 + a.sum1 ? a.left1 + b.left1 : a.left1,
            b.right0 == b.sum0 + b.sum1 ? b.right0 + a.right0 : b.right0,b.right1 == b.sum0 + b.sum1 ? b.right1 + a.right1 : b.right1
        };
    }
};
struct F {
    int op;
    static F e() { return{ 0 }; }
    T operator()(const T &x) {
        if (op == 0) return x;
        else if (op == 1) return {
            x.sum0 + x.sum1,0,
            x.sum0 + x.sum1,0,
            x.sum0 + x.sum1,0,
            x.sum0 + x.sum1,0
        };
        else if (op == 2) return{
            0,x.sum0 + x.sum1,
            0,x.sum0 + x.sum1,
            0,x.sum0 + x.sum1,
            0,x.sum0 + x.sum1
        };
        else return{
            x.sum1,x.sum0,
            x.max1,x.max0,
            x.left1,x.left0,
            x.right1,x.right0
        };
    }
    F operator() (const F &g) {
        if (op == 0) return g;
        else if (op == 1) return { 1 };
        else if (op == 2) return { 2 };
        else {
            if (g.op == 0) return { 3 };
            else if (g.op == 1) return { 2 };
            else if (g.op == 2) return { 1 };
            else return { 0 };
        }
    }
};

template<class T, class F>
class SegmentTreeLazy {
    int n;
    vector<T> node;
    vector<F> lazy;

    void push_down(int rt) {
        node[rt << 1] = lazy[rt](node[rt << 1]);
        lazy[rt << 1] = lazy[rt](lazy[rt << 1]);
        node[rt << 1 | 1] = lazy[rt](node[rt << 1 | 1]);
        lazy[rt << 1 | 1] = lazy[rt](lazy[rt << 1 | 1]);
        lazy[rt] = F::e();
    }

    void update(int rt, int l, int r, int x, int y, F f) {
        if (r < x || y < l) return;
        if (x <= l && r <= y) return node[rt] = f(node[rt]), lazy[rt] = f(lazy[rt]), void();
        push_down(rt);
        int mid = l + r >> 1;
        update(rt << 1, l, mid, x, y, f);
        update(rt << 1 | 1, mid + 1, r, x, y, f);
        node[rt] = node[rt << 1] + node[rt << 1 | 1];
    }

    T query(int rt, int l, int r, int x, int y) {
        if (r < x || y < l) return T::e();
        if (x <= l && r <= y) return node[rt];
        push_down(rt);
        int mid = l + r >> 1;
        return query(rt << 1, l, mid, x, y) + query(rt << 1 | 1, mid + 1, r, x, y);
    }

public:
    SegmentTreeLazy(int _n = 0) { init(_n); }
    SegmentTreeLazy(int _n, const vector<T> &src) { init(_n, src); }
    void init(int _n) {
        n = _n;
        node.assign(n << 2, T::e());
        lazy.assign(n << 2, F::e());
    }
    void init(int _n, const vector<T> &src) {
        init(_n);
        function<void(int, int, int)> build = [&](int rt, int l, int r) {
            if (l == r) return node[rt] = src[l], void();
            int mid = l + r >> 1;
            build(rt << 1, l, mid);
            build(rt << 1 | 1, mid + 1, r);
            node[rt] = node[rt << 1] + node[rt << 1 | 1];
        };
        build(1, 1, n);
    }

    void update(int x, int y, const F &f) { update(1, 1, n, x, y, f); }

    T query(int x, int y) { return query(1, 1, n, x, y); }
};

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n, m;
    cin >> n >> m;
    vector<T> a(n + 1);
    for (int i = 1;i <= n;i++) {
        int x;
        cin >> x;
        a[i] = { 1 - x,x,1 - x,x,1 - x,x,1 - x,x };
    }
    SegmentTreeLazy<T, F> sgt(n, a);
    while (m--) {
        int op, l, r;
        cin >> op >> l >> r;
        l++, r++;
        if (op == 0) sgt.update(l, r, { 1 });
        else if (op == 1) sgt.update(l, r, { 2 });
        else if (op == 2) sgt.update(l, r, { 3 });
        else if (op == 3) cout << sgt.query(l, r).sum1 << '\n';
        else cout << sgt.query(l, r).max1 << '\n';
    }
    return 0;
}

相關文章