Educational Codeforces Round 33 (Rated for Div. 2) F. Subtree Minimum Query(主席樹合併)
阿新 • • 發佈:2018-12-30
題意
給定一棵 \(n\) 個點的帶點權樹,以 \(1\) 為根, \(m\) 次詢問,每次詢問給出兩個值 \(p, k\) ,求以下值:
\(p\) 的子樹中距離 \(p \le k\) 的所有點權最小值,詢問強制線上。
\(n \le 10^5 , m \le 10^6, TL = 6s\)
題解
如果不強制線上,直接線段樹合併就做完了。
強制線上,不難想到用一些可持久化的結構來維護這些東西。
其實可以類似線段樹合併那樣考慮,也就是說每次合併的時候我們依然使用兒子的資訊。
只要在合併 \(x, y\) 共有部分的時候建出新節點,然後權值是 \(x, y\) 權值的較小值,其他的部分直接連向那些單獨有的子樹資訊即可。
其實實現是這樣的:
int Merge(int x, int y) {
if (!x || !y) return x | y;
int o = Newnode();
ls[o] = Merge(ls[x], ls[y]);
rs[o] = Merge(rs[x], rs[y]);
minv[o] = min(minv[x], minv[y]);
return o;
}
這樣的話,既可以保留子樹資訊,又可以得到這個節點新的資訊。
最後空間複雜度就是 \(O(n \log n)\) ,時間複雜度是 \(O((n + m) \log n)\)
總結
強制線上問題,用可持久化資料結構去解決就行了,也就是把平常的資料結構記歷史版本,並儘量用之前的資訊。
程式碼
#include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl #define DEBUG(...) fprintf(stderr, __VA_ARGS__) #define pb push_back using namespace std; template<typename T> inline bool chkmin(T &a, T b) {return b < a ? a = b, 1 : 0;} template<typename T> inline bool chkmax(T &a, T b) {return b > a ? a = b, 1 : 0;} inline int read() { int x(0), sgn(1); char ch(getchar()); for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * sgn; } void File() { #ifdef zjp_shadow freopen ("F.in", "r", stdin); freopen ("F.out", "w", stdout); #endif } const int N = 1e5 + 1e3, inf = 0x3f3f3f3f; #define lson ls[o], l, mid #define rson rs[o], mid + 1, r template<int Maxn> struct Chair_Man_Tree { int ls[Maxn], rs[Maxn], minv[Maxn], Size; Chair_Man_Tree() { minv[0] = inf; } inline int Newnode() { int o = ++ Size; minv[o] = inf; return o; } void Update(int &o, int l, int r, int up, int uv) { if (!o) o = Newnode(); if (l == r) { chkmin(minv[o], uv); return ; } int mid = (l + r) >> 1; up <= mid ? Update(lson, up, uv) : Update(rson, up, uv); minv[o] = min(minv[ls[o]], minv[rs[o]]); } int Query(int o, int l, int r, int ql, int qr) { if (!o) return inf; if (ql <= l && r <= qr) return minv[o]; int mid = (l + r) >> 1; if (qr <= mid) return Query(lson, ql, qr); if (ql > mid) return Query(rson, ql, qr); return min(Query(lson, ql, qr), Query(rson, ql, qr)); } int Merge(int x, int y) { if (!x || !y) return x | y; int o = Newnode(); ls[o] = Merge(ls[x], ls[y]); rs[o] = Merge(rs[x], rs[y]); minv[o] = min(minv[x], minv[y]); return o; } }; vector<int> G[N]; int val[N], rt[N], dep[N]; int n, S; Chair_Man_Tree<N * 80> T; void Dfs(int u, int fa = 0) { dep[u] = dep[fa] + 1; for (int v : G[u]) if (v != fa) Dfs(v, u), rt[u] = T.Merge(rt[u], rt[v]); T.Update(rt[u], 1, n, dep[u], val[u]); } int main () { File(); n = read(); S = read(); For (i, 1, n) val[i] = read(); For (i, 1, n - 1) { int u = read(), v = read(); G[u].pb(v); G[v].pb(u); } Dfs(S); int q = read(), ans = 0; For (i, 1, q) { int x = (read() + ans) % n + 1, k = (read() + ans) % n; printf ("%d\n", ans = T.Query(rt[x], 1, n, dep[x], min(dep[x] + k, n))); } return 0; }