F 小红的01串(六)

碎碎念:第一眼看题意,这不是非常经典的线段树么。然后开始写,都把 push_down 写完了,结果发现 的,要加个离散化才行。

建议正在学线段树的同学都可以做做这题,如果能独立把代码调出来的话相信你的收获一定会很大。

我们先思考 左右的情况。我们可以开一个线段树,维护以下信息:

  • 将当前区间修改为 开头的好串和 开头的好串的最小修改次数(分别记作

首先考虑合并操作,我们在 push_up 的时候,当前节点的 就是左子树的 加上右子树的 或者 ,取决于左子树维护的区间长度。如果是奇数,说明 开头的好串一定是 结尾,那么右子树需要从 开头,因此需要用 更新,另外一个情况同理。

再考虑更新操作,注意到有两个区间修改操作,先考虑单种:

  • 若只有区间修改为 的话可以考虑加个 ,然后直接更新区间
  • 若只有区间取反,可以考虑加个 ,然后交换区间 即可。

接下来考虑复合操作:

  • 若对一个区间先取反后修改为 ,相当于取反操作无效,只用管修改操作。
  • 若对一个区间先修改为 后再取反,相当于区间修改为 ,直接更新

因此我们看得出来,对于一个区间的两个 ,他们的先后顺序是有区别的,因此我们可以将两个 记作是第几次操作,方便我们更新。

这里需要注意一点:若我们在 push_down 的过程中,发现离当前区间的操作的最近操作是取反,并且当前又更新了一个取反操作,那么我们应该要做的是把取反的 给取消掉,而不是更新取反的 ,否则会影响下面子区间的取反操作。

好了,分析完以后,发现 ,所以我们不能像刚刚一样每个叶节点只维护一个位置,因此要让每个叶节点维护一个区间,例如我们的操作区间依次是 ,先变成左闭右开的区间,即 ,把端点记下来也就是 ,最后将相邻两个数之间合并成一个左闭右开的区间即可。 。离散化后就可以转化了。

时间复杂度

#include <bits/stdc++.h>
#define ls (u << 1)
#define rs (u << 1 | 1)
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 3e5 + 10;

struct qy {
    int opt, l, r;
} a[N];
struct node {
    int l, r;       // 管理的左右区间,用离散化之后的值代替
    int ranL, ranR; // 表示管理的左右区间
    int num0, num1; // 将当前区间修改为0开头的好串和1开头的好串的修改次数
    int lazy_tag;   // 更新区间翻转的lazy是第几次操作
    int lazy_1;     // 更新区间为1的lazy是第几次操作
} tr[N << 2];

vector<int> v;
int getIdx(int x) {
    return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}

void calc(int u, int lt, int l1) {
    int len = tr[u].ranR - tr[u].ranL + 1;
    if (lt && l1 && lt > l1) {
        // 先1后翻,等价于赋0
        tr[u].num0 = len / 2;
        tr[u].num1 = (len + 1) / 2;
    } else if (l1) {
        // 剩下的情况就是翻后1或者有1无翻,直接赋值为1
        tr[u].num0 = (len + 1) / 2;
        tr[u].num1 = len / 2;
    } else if (lt) {
        swap(tr[u].num0, tr[u].num1);
    }
}

void push_down(int u) {
    if (tr[u].lazy_1) tr[ls].lazy_1 = tr[rs].lazy_1 = tr[u].lazy_1;
    if (tr[u].lazy_tag) {
        // down下去取反操作时,注意看最近的操作是不是取反
        // 如果是的话就需要更新子区间的lazy_tag为0
        if (tr[ls].lazy_1 < tr[ls].lazy_tag) tr[ls].lazy_tag = 0;
        else tr[ls].lazy_tag = tr[u].lazy_tag;
        if (tr[rs].lazy_1 < tr[rs].lazy_tag) tr[rs].lazy_tag = 0;
        else tr[rs].lazy_tag = tr[u].lazy_tag;
    }
    calc(ls, tr[u].lazy_tag, tr[u].lazy_1);
    calc(rs, tr[u].lazy_tag, tr[u].lazy_1);
    tr[u].lazy_1 = tr[u].lazy_tag = 0;
}

void push_up(int u) {
    tr[u].ranL = tr[ls].ranL, tr[u].ranR = tr[rs].ranR;
    // 更新num0
    tr[u].num0 = tr[ls].num0;
    int len_l = tr[ls].ranR - tr[ls].ranL + 1;
    if (len_l & 1) tr[u].num0 += tr[rs].num1;
    else tr[u].num0 += tr[rs].num0;
    // 更新num1
    tr[u].num1 = tr[ls].num1;
    if (len_l & 1) tr[u].num1 += tr[rs].num0;
    else tr[u].num1 += tr[rs].num1;
}

void build(int u, int l, int r) {
    tr[u].l = l, tr[u].r = r;
    if (l == r) {
        tr[u].ranL = v[l - 1];
        tr[u].ranR = v[l] - 1;
        int len = tr[u].ranR - tr[u].ranL + 1;
        tr[u].num0 = len / 2;
        tr[u].num1 = (len + 1) / 2;
        return ;
    }
    int mid = (l + r) >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
    push_up(u);
}

void update(int u, int st, int ed, int opt, int num_opt) {
    // opt=1表示更新当前区间为1,否则就是翻转
    int l = tr[u].l, r = tr[u].r;
    if (st <= l && r <= ed) {
        if (opt == 1) {
            tr[u].lazy_1 = num_opt;
            calc(u, 0, num_opt);
        }
        if (opt == 2) {
            if (tr[u].lazy_1 < tr[u].lazy_tag) tr[u].lazy_tag = 0;
            else tr[u].lazy_tag = num_opt;
            calc(u, num_opt, 0);
        }
        return ;
    }
    push_down(u);
    int mid = (l + r) / 2;
    if (st <= mid) update(ls, st, ed, opt, num_opt);
    if (ed > mid) update(rs, st, ed, opt, num_opt);
    push_up(u);
}

node query(int u, int st, int ed) {
    int l = tr[u].l, r = tr[u].r;
    if (st <= l && r <= ed) return tr[u];
    push_down(u);
    int mid = (l + r) >> 1;
    node ans;
    ans.num0 = -1;
    if (st <= mid) {
        node tmp = query(ls, st, ed);
        ans = tmp;
    }
    if (ed > mid) {
        node tmp = query(rs, st, ed);
        if (ans.num0 == -1) ans = tmp;
        else {
            // 更新答案的num0
            int lenl = ans.ranR - ans.ranL + 1;
            if (lenl & 1) ans.num0 += tmp.num1;
            else ans.num0 += tmp.num0;
            // 更新答案的num1
            if (lenl & 1) ans.num1 += tmp.num0;
            else ans.num1 += tmp.num1;
            ans.ranR = tmp.ranR;
        }
    }
    return ans;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
#endif
    int m, q;
    cin >> m >> q;
    for (int i = 1; i <= q; i ++ ) {
        cin >> a[i].opt >> a[i].l >> a[i].r;
        v.push_back(a[i].l);
        v.push_back(a[i].r + 1);
    }
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());
    int n = v.size() - 1;
    build(1, 1, n);
    for (int i = 1; i <= q; i ++ ) {
        int opt = a[i].opt;
        int l = getIdx(a[i].l) , r = getIdx(a[i].r + 1) - 1;
        if (opt == 1) update(1, l, r, 1, i);
        else if (opt == 2) update(1, l, r, 2, i);
        else {
            node ans = query(1, l, r);
            cout << min(ans.num0, ans.num1) << endl;
        }
    }
    return 0;
}