【luogu CF1039E】Summer Oenothera Exhibition(貪心)(根號分治)(LCT)
阿新 • • 發佈:2022-03-24
Summer Oenothera Exhibition
題目連結:luogu CF1039E
題目大意
給你一個數組,然後每次問你一個 k,問你能把陣列最少分成多少段,使得每一段的極差不超過 k。
思路
首先考慮對於一次詢問怎麼做。
考慮到一個貪心的想法,就是一個區間能往右擴一點是一點。
不難發現這樣是正確的。
然後你考慮是否可以有一些東西優化這個過程。
那你每個地方你可以通過二分+區間求極差【可以用 ST 表】得到它作為一段的左邊的話下一個段應該從哪裡開始 \(nxt_i\)。
然後你會發現如果我們把它連邊,它可以相當於要求從 \(1\) 到 \(n+1\) 點的路徑長度。
那這個加邊的我們可以用 DP 一下得到。
那考慮有沒有一些東西方便擴充套件的,那 \(k\)
但是你每次可能都要更改每個邊的 \(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<cmath> #include<cstdio> #include<vector> #include<iostream> #include<algorithm> 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 <int> 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; }