CodeForces 1110F Nearest Leaf | 線段樹/換根
阿新 • • 發佈:2019-02-10
enter typedef 編號 struct return vector chan get tor 。
我……又詐屍了……
代碼幾乎都不會寫了,打場CF居然上分啦,開心!(雖然還是比不過列表裏的各路神仙)
題目鏈接
題目描述
一棵\(n\)個點的有根樹,規定一種dfs序(規則:編號小的點優先dfs),\(m\)次詢問一個點\(u\)和一個區間\([l, r]\),求dfs序在這個區間內的葉子中,到\(u\)最小的距離。
\(n, m \le 500000\)
題解
這題……很簡單……
題面一上來給個什麽歐拉遍歷定義……我嚇得比賽中沒看這題……(實際上碼量對於代碼幾乎都不會敲的退役選手來說,不是非常友好 = = 當時做了可能也會寫跪)
用線段樹維護所有葉子到“當前點”(一開始是\(1\)號節點)的距離\(dis\)
一開始以\(1\)號節點為“當前點”,dfs求距離,建樹。這樣\(u = 1\)的詢問就可以解決了。
怎麽解決其他\(u\)的詢問呢?考慮移動“當前點”時,線段樹會如何變化。
因為是DFS序,所以每棵子樹在dfs序上都挨在一起。當“當前點”從父親\(u\)移到兒子\(v\)\(w(u, v)\)時,子樹\(v\)內所有點的\(dis\)都減去了\(w(u, v)\)(<u, v>這條邊的長度),而子樹\(v\)外的所有點\(dis\)都加上了\(w(u, v)\)。只需在線段樹上區間修改即可。
顯然,先把詢問都讀進來,離線處理非常優秀。空間允許的話似乎也可以主席樹?(沒試過 = =)
代碼
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <iostream> #include <cassert> #include <vector> #define space putchar(' ') #define enter putchar('\n') using namespace std; typedef long long ll; template <class T> void read(T &x){ char c; bool op = 0; while(c = getchar(), c < '0' || c > '9') if(c == '-') op = 1; x = c - '0'; while(c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; if(op) x = -x; } template <class T> void write(T x){ if(x < 0) putchar('-'), x = -x; if(x >= 10) write(x / 10); putchar('0' + x % 10); } const int N = 500005; const ll INF = 0x3f3f3f3f3f3f3f3f; int n, m; int ncnt, dfn[N], idx[N], ed[N]; int lcnt, llst[N], lnum[N], prel[N], nxtl[N]; ll w[N], dis[N], data[4*N], lazy[4*N], ans[N]; vector <int> son[N]; struct Query { int id, u, l, r; bool operator < (const Query &b) const { return dfn[u] < dfn[b.u]; } } qry[N]; int qpos = 1; void dfs1(int u, int pre){ dfn[u] = ++ncnt; idx[ncnt] = u; for(auto v : son[u]){ dis[v] = dis[u] + w[v]; dfs1(v, u); } if(u != 1 && ncnt == dfn[u]){ lnum[u] = ++lcnt; llst[lcnt] = u; } ed[u] = ncnt; } void modify(int k, ll x){ data[k] += x, lazy[k] += x; } void pushdown(int k){ if(!lazy[k]) return; modify(k << 1, lazy[k]); modify(k << 1 | 1, lazy[k]); lazy[k] = 0; } void change(int k, int l, int r, int ql, int qr, ll x){ if(ql <= l && qr >= r) return void(modify(k, x)); pushdown(k); int mid = (l + r) >> 1; if(ql <= mid) change(k << 1, l, mid, ql, qr, x); if(qr > mid) change(k << 1 | 1, mid + 1, r, ql, qr, x); data[k] = min(data[k << 1], data[k << 1 | 1]); } ll query(int k, int l, int r, int ql, int qr){ if(ql <= l && qr >= r) return data[k]; pushdown(k); int mid = (l + r) >> 1; ll ret = INF; if(ql <= mid) ret = query(k << 1, l, mid, ql, qr); if(qr > mid) ret = min(ret, query(k << 1 | 1, mid + 1, r, ql, qr)); return ret; } void build_tree(int k, int l, int r){ if(l == r) return void(data[k] = dis[llst[l]]); int mid = (l + r) >> 1; build_tree(k << 1, l, mid); build_tree(k << 1 | 1, mid + 1, r); data[k] = min(data[k << 1], data[k << 1 | 1]); } void dfs2(int u, int pre){ while(qry[qpos].u == u){ ans[qry[qpos].id] = query(1, 1, lcnt, qry[qpos].l, qry[qpos].r); qpos++; } for(auto v : son[u]){ change(1, 1, lcnt, 1, lcnt, w[v]); change(1, 1, lcnt, nxtl[dfn[v]], prel[ed[v]], -2 * w[v]); dfs2(v, u); change(1, 1, lcnt, 1, lcnt, -w[v]); change(1, 1, lcnt, nxtl[dfn[v]], prel[ed[v]], 2 * w[v]); } } int main(){ read(n), read(m); for(int i = 2, u; i <= n; i++){ read(u), read(w[i]); son[u].push_back(i); } dfs1(1, 0); for(int i = 1, t = 1; i <= n; i++){ if(lnum[idx[i]]) t = lnum[idx[i]]; prel[i] = t; } for(int i = n, t = lcnt; i; i--){ if(lnum[idx[i]]) t = lnum[idx[i]]; nxtl[i] = t; } for(int i = 1; i <= m; i++){ qry[i].id = i, read(qry[i].u), read(qry[i].l), read(qry[i].r); qry[i].l = nxtl[qry[i].l], qry[i].r = prel[qry[i].r]; } sort(qry + 1, qry + m + 1); build_tree(1, 1, lcnt); dfs2(1, 0); for(int i = 1; i <= m; i++) write(ans[i]), enter; return 0; }
CodeForces 1110F Nearest Leaf | 線段樹/換根