1 条题解
-
0
这题如果不带修有一个 的倍增做法,但是其实和正解没啥关系。
考虑将相同的数缩成若干个连续段 。
如果出现了一个谷 ,那么我们要把两边并起来,至少要把 变成 。
设 。如果 ,那么我们可以把 提升到 ,和两边其中一个合并。
否则两边不可能并起来,把 分给两边然后继续做,相当于把 删除,然后插入 $(\min(x_{i - 1}, x_{i + 1}), \left\lfloor\frac{y_i}{2^d}\right\rfloor), (+\infty, 1), (\min(x_{i - 1}, x_{i + 1}), \left\lfloor\frac{y_i}{2^d}\right\rfloor)$。
这样整个序列就变成了一个单峰序列,连续段个数是 的。在消除谷的过程中可以把 计入答案。求答案就在序列两端分别添加一个 然后不停做上面的过程即可。
发现这个信息是可以合并的,所以可以上线段树支持单点修改。合并两个信息就把右边信息的所有连续段逐个加入到左边信息中。一个连续段添加到一个信息中,就看有没有新产生谷,如果产生了就递归加入新的连续段。
时间复杂度 ,空间复杂度 。
以下的代码在 QOJ 同时获得了最短解和最优解。
#include <bits/stdc++.h> using namespace std; int n, N; struct node { int t, k, a[22][2]; node(int x = 1e9) { t = (x < 1e8 ? x : 0); a[k = 1][0] = x; a[k][1] = 1; } void add(int x, int y) { if (!y) { return; } while (k >= 2 && a[k - 1][0] > a[k][0] && a[k][0] < x) { int p = a[k][0], q = a[k][1]; if (a[--k][0] > 1e8 && x > 1e8) { continue; } int d = min(a[k][0], x) - p; add(p + d, q >> d); if (q & ((1 << d) - 1)) { add(1e9, 1); add(p + d, q >> d); } } if (k && a[k][0] == x) { a[k][1] += y; } else { a[++k][0] = x; a[k][1] = y; } if (a[k][0] < 1e8) { t = max(t, a[k][0] + __lg(a[k][1])); } } } a[262199]; inline node operator + (node a, node b) { a.t = max(a.t, b.t); for (int i = 1; i <= b.k; ++i) { a.add(b.a[i][0], b.a[i][1]); } return a; } void prepare_game(vector<int> b) { n = (int)b.size(); N = 1 << (__lg(n + 1) + 1); for (int i = 1; i <= n; ++i) { a[i + N] = node(b[i - 1]); } for (int i = N; i; --i) { a[i] = a[i << 1] + a[i << 1 | 1]; } } int play_game(int l, int r) { node u(1e9), v(1e9); for (l += N, r += N + 2; l ^ r ^ 1; l >>= 1, r >>= 1) { if (!(l & 1)) { u = u + a[l ^ 1]; } if (r & 1) { v = a[r ^ 1] + v; } } return (u + v).t; } void update_game(int x, int y) { a[x += N + 1] = node(y); while (x >>= 1) { a[x] = a[x << 1] + a[x << 1 | 1]; } }
- 1
信息
- ID
- 7403
- 时间
- 4000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者