【luogu CF1039E】Summer Oenothera Exhibition(贪心)(根号分治)(LCT)
Summer Oenothera Exhibition
题目链接:luogu CF1039E
题目大意
给你一个数组,然后每次问你一个 k,问你能把数组最少分成多少段,使得每一段的极差不超过 k。
思路
首先考虑对于一次询问怎么做。
考虑到一个贪心的想法,就是一个区间能往右扩一点是一点。
不难发现这样是正确的。
然后你考虑是否可以有一些东西优化这个过程。
那你每个地方你可以通过二分+区间求极差【可以用 ST 表】得到它作为一段的左边的话下一个段应该从哪里开始 \(nxt_i\)。
然后你会发现如果我们把它连边,它可以相当于要求从 \(1\) 到 \(n+1\) 点的路径长度。
那这个加边的我们可以用 DP 一下得到。
那考虑有没有一些东西方便扩展的,那 \(k\) 修改了边可能会变,那我们也可以用 LCT 来维护。
但是你每次可能都要更改每个边的 \(nxt_i\) 导致修改次数是 \(n^2\) 复杂度是 \(n^2\log n\) 甚至不如暴力。
那我们考虑根号分治。
你会发现暴力跳的问题就是每次跳的距离太短,LCT 跳的问题就是修改的次数太多了。
那我们 \(\leqslant B\) 的我们用 LCT,那这样每个数的 \(nxt_i\) 顶多修改 \(B\) 次。
然后大于的用暴力做,那每次这么跳的次数是 \(\dfrac{n}{B}\)。
那 \(B=\sqrt{n}\) 就可以做到 \(O(n\sqrt{n}\log n)\)
然后接着简单讲讲实现,考虑记录每个每个位置记录它是怎么跳的。
然后 LCT 的部分就不说了,说说怎么修改 \(nxt_i\)。
考虑算出每个位置下一次修改是在那个询问,然后放进这个询问的 vector 里面。
然后每次看询问之间先把 vector 里面的搞出来修改了。
然后更新就是暴力加出位置,然后再用 lower_bound 得到下次在哪个询问。
代码
#include
#include
#include
#include
#include
using namespace std;
const int N = 1e5 + 100;
const int B = 230;
struct node {
int id, k;
}s[N];
int n, w, q, a[N], log2_[N], nxt[N];
int maxn[N][18], minn[N][18], xl[N];
int ans[N];
vector st[N];
bool bl[N];
bool cmp(node x, node y) {
return x.k < y.k;
}
int clac(int l, int r) {
if (r == n + 1) return 2e9;
int k = log2_[r - l + 1];
return max(maxn[l][k], maxn[r - (1 << k) + 1][k]) - min(minn[l][k], minn[r - (1 << k) + 1][k]);
}
int get_nxt(int now, int k) {
int l = now, r = n, re;
while (l <= r) {
int mid = (l + r) >> 1;
if (clac(now, mid) <= k) re = mid, l = mid + 1;
else r = mid - 1;
}
return re + 1;
}
struct LCT {
int sz[N], ls[N], rs[N], fa[N];
bool nrt(int x) {return ls[fa[x]] == x || rs[fa[x]] == x;}
bool lrs(int x) {return ls[fa[x]] == x;}
void up(int x) {sz[x] = sz[ls[x]] + sz[rs[x]] + 1;}
void rotate(int x) {
int y = fa[x], z = fa[y];
int b = lrs(x) ? rs[x] : ls[x];
if (z && nrt(y)) (lrs(y) ? ls[z] : rs[z]) = x;
if (lrs(x)) rs[x] = y, ls[y] = b;
else ls[x] = y, rs[y] = b;
fa[x] = z; fa[y] = x;
if (b) fa[b] = y;
up(y); up(x);
}
void Splay(int x) {
while (nrt(x)) {
if (nrt(fa[x])) {
if (lrs(x) == lrs(fa[x])) rotate(fa[x]);
else rotate(x);
}
rotate(x);
}
}
void access(int x) {
int lst = 0;
for (; x; lst = x, x = fa[x]) {
Splay(x);
rs[x] = lst;
up(x);
}
}
int find_root(int x) {
access(x); Splay(x);
while (ls[x]) x = ls[x];
Splay(x);
return x;
}
void link(int x, int y) {
access(x); fa[x] = y;
}
void cut(int x, int y) {
access(x); Splay(y);
fa[x] = 0; rs[y] = 0;
up(y);
}
}T;
int main() {
scanf("%d %d %d", &n, &w, &q);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= q; i++) {
scanf("%d", &s[i].k); s[i].k = w - s[i].k;
s[i].id = i;
}
sort(s + 1, s + q + 1, cmp);
for (int i = 1; i <= q; i++) xl[i] = s[i].k;
for (int i = 1; i <= n; i++) maxn[i][0] = minn[i][0] = a[i];
log2_[0] = -1; for (int i = 1; i <= n; i++) log2_[i] = log2_[i >> 1] + 1;
for (int i = 1; i <= 17; i++)
for (int j = 1; j + (1 << i) - 1 <= n; j++) {
maxn[j][i] = max(maxn[j][i - 1], maxn[j + (1 << (i - 1))][i - 1]);
minn[j][i] = min(minn[j][i - 1], minn[j + (1 << (i - 1))][i - 1]);
}
for (int i = 1; i <= n; i++) st[1].push_back(i), nxt[i] = i;
for (int i = 1; i <= q; i++) {
for (int j = 0; j < st[i].size(); j++) {
int now = st[i][j], fr = nxt[now] + 1;
T.cut(now, nxt[now]);
while (fr <= n && fr - now <= B && clac(now, fr) <= s[i].k) fr++;
if (fr - now > B) bl[now] = 1;
else {
int to = lower_bound(xl + 1, xl + q + 1, clac(now, fr)) - xl;
nxt[now] = fr; T.link(now, nxt[now]);
st[to].push_back(now);
}
}
for (int j = 1; j <= n; j = get_nxt(j, s[i].k)) {
if (!bl[j]) {
T.access(j); T.Splay(j); ans[s[i].id] += T.sz[j] - 1;
j = T.find_root(j);
}
if (j == n + 1) break;
ans[s[i].id]++;
}
}
for (int i = 1; i <= q; i++) printf("%d\n", ans[i] - 1);
return 0;
}