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;
}

浙公网安备 33010602011771号