Codeforces 1705E Mark and Professor Koro 题解 [ 蓝 ] [ 线段树上二分 ]

Mark and Professor Koro:比较简单的线段树上二分题。

先来记录一下区间 \([l,r]\) 内线段树二分的流程:

  • 如果当前节点在 \([l, r]\) 内:
    • 判断该区间是否有解
      • 若无解,直接返回 \(-1\)
      • 若有解,依次递归左右儿子节点,找到答案则返回。
  • 否则判断左右儿子与 \([l, r]\) 是否有交,然后依次递归左右儿子节点,找到答案则返回。

注意到“若有解,依次递归左右儿子节点,找到答案则返回”与“否则判断左右儿子与 \([l, r]\) 是否有交,然后依次递归左右儿子节点,找到答案则返回”这两步实际上在做同一件事,所以可以将这两段代码合并。

对于本题,贪心地考虑,每个数能操作就操作一定最优。所以最终形成的序列每个数一定 \(< 2\)

因此先预处理出初始的最优状态,然后考虑让一个位置减去 \(1\) 和让一个位置加上 \(1\) 的变化:

  • 让一个位置减去 \(1\):
    • 找到 \(pos\) 后的第一个是 \(1\) 的位置(包含 \(pos\) 本身),令这个位置为 \(x\),然后将 \(ans_x\) 的值设为 \(0\)\(ans_{pos\sim x - 1}\) 设为 \(1\)。相当于模拟退位操作。
  • 让一个位置加上 \(1\):
    • 找到 \(pos\) 后的第一个是 \(0\) 的位置(包含 \(pos\) 本身),令这个位置为 \(x\),然后将 \(ans_x\) 的值设为 \(1\)\(ans_{pos\sim x - 1}\) 设为 \(0\)。相当于模拟进位操作。

找到 \(pos\) 后第一个是 \(0/1\) 的位置可以用线段树上二分实现,区间推平可以用懒标记实现。总体时间复杂度 \(O(n\log n)\)

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc (p << 1)
#define rc ((p << 1) | 1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 300005, MXV = 300000;
int n, q, a[N], tot[N];
struct Node{
    int l, r, sm[2], tag = -1, mx;
};
struct Segtree{
    Node tr[4 * N];
    void pushup(int p)
    {
        tr[p].sm[0] = (tr[lc].sm[0] | tr[rc].sm[0]);
        tr[p].sm[1] = (tr[lc].sm[1] | tr[rc].sm[1]);
        tr[p].mx = max(tr[lc].mx, tr[rc].mx);
    }
    void pushdown(int p)
    {
        if(tr[p].tag != -1)
        {
            tr[lc].tag = tr[p].tag;
            tr[rc].tag = tr[p].tag;
            tr[lc].sm[0] = tr[p].tag;
            tr[rc].sm[0] = tr[p].tag;
            tr[lc].sm[1] = (tr[p].tag ^ 1);
            tr[rc].sm[1] = (tr[p].tag ^ 1);
            tr[lc].mx = (tr[p].tag ? tr[lc].r : 0);
            tr[rc].mx = (tr[p].tag ? tr[rc].r : 0);
        }
        tr[p].tag = -1;
    }
    void build(int p, int ln, int rn)
    {
        tr[p] = {ln, rn, {tot[ln], tot[ln] ^ 1}, -1, (tot[ln] ? ln : 0)};
        if(ln == rn) return;
        int mid = (ln + rn) >> 1;
        build(lc, ln, mid);
        build(rc, mid + 1, rn);
        pushup(p);
    }
    void update(int p, int ln, int rn, int v)
    {
        if(ln <= tr[p].l && tr[p].r <= rn)
        {
            tr[p].sm[0] = v;
            tr[p].sm[1] = (v ^ 1);
            tr[p].mx = (v ? tr[p].r : 0);
            tr[p].tag = v;
            return;
        }
        pushdown(p);
        int mid = (tr[p].l + tr[p].r) >> 1;
        if(ln <= mid) update(lc, ln, rn, v);
        if(rn >= mid + 1) update(rc, ln, rn, v);
        pushup(p);
    }
    int binary(int p, int ln, int rn, int v)
    {
        if(ln <= tr[p].l && tr[p].r <= rn)
        {
            if(tr[p].sm[v] == 0) return -1;
            if(tr[p].l == tr[p].r) return tr[p].l;
        }
        pushdown(p);
        int mid = (tr[p].l + tr[p].r) >> 1;
        if(ln <= mid)
        {
            int res = binary(lc, ln, rn, v);
            if(res != -1) return res;
        }
        if(rn >= mid + 1)
        {
            int res = binary(rc, ln, rn, v);
            if(res != -1) return res;
        }
        return -1;
    }
}tr1;
int main()
{
    // freopen("CF1705E.in", "r", stdin);
    // freopen("CF1705E.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> q;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        tot[a[i]]++;
    }
    for(int i = 1; i <= MXV; i++)
    {
        tot[i + 1] += tot[i] / 2;
        tot[i] = (tot[i] % 2);
    }
    tr1.build(1, 1, MXV);
    for(int i = 1; i <= q; i++)
    {
        int x, y;
        cin >> x >> y;
        int tmp = tr1.binary(1, a[x], MXV, 0);
        if(tmp != -1)
        {
            tr1.update(1, tmp, tmp, 0);
            if(a[x] <= tmp - 1) tr1.update(1, a[x], tmp - 1, 1);
        }
        a[x] = y;
        tmp = tr1.binary(1, a[x], MXV, 1);
        if(tmp != -1)
        {
            tr1.update(1, tmp, tmp, 1);
            if(a[x] <= tmp - 1) tr1.update(1, a[x], tmp - 1, 0);
        }
        cout << tr1.tr[1].mx << "\n";
    }
    return 0;
}
posted @ 2025-09-06 00:37  KS_Fszha  阅读(15)  评论(0)    收藏  举报