題解 P1600 [NOIP2016 提高組] 天天愛跑步
阿新 • • 發佈:2021-09-06
題解 P1600 [NOIP2016 提高組] 天天愛跑步
的點有多少個的話要怎麼做。
的詢問拆成 \((1,u)-(1,p)\) 的上行的詢問和 \((1,v)-(1,fa_p)\) 的下行的詢問(\(-\) 這裡代表答案減去,欽定 \(1\) 為根節點),然後最後開一個桶, \(dfs\) 累加答案即可。
題意簡述
給定一棵 \(n\) 個節點的樹,第 \(i\) 個點有一個權值 \(w_i\) ,有 \(m\) 次操作,每次操作讓 \((u,v)\) 路徑上的滿足 \(\operatorname{dist}(u,x)=w_x\) (\(\operatorname{dist}(x,y)\) 表示 \(x\) 到 \(y\) 的距離)的 \(x\) 的答案 \(+1\) ,最後輸出所有點的答案。
\(1 \leq n,m \leq 3\times 10^5 ,0 \leq w_i\leq n\) 。
Solution
先考慮如果是每一次操作求有多少個滿足 \(\operatorname{dist}(u,x)=w_x\)
設 \(u,v\) 的 \(lca\) 是 \(p\) ,那麼對於 \((u,p)\)這一段路徑上的點 \(x\) 如果滿足條件就相當於(下面稱這種情況為上行):
\[dep_x-dep_u=w_x \\ dep_u=w_x+dep_x \]對於 \((p,v)\) 這一段路徑上的點(不包括 \(p\))點 \(x\) 如果滿足條件就相當於(下面稱這種情況為下行):
\[dep_x+dep_u-2dep_{lca}=w_x \\ dep_u-2dep_{lca}=w_x-dep_x \]問題就轉化成了詢問路徑上權值等於一個數的點的個數,可以用主席樹。
當然主席樹是大材小用,可以直接樹上差分,把 \((u,v)\)
回到題目,對於每個點單獨詢問怎麼做。
還是考慮樹上差分,類似於上面的拆的方式,我們可以發現,只有 \(u\) 的子樹裡面的操作對 \(u\) 的答案有影響。
那麼可以把上面的把點權插入到桶,遇到操作查詢 換成 把操作插入到桶,\(dfs\) 到點的時候查詢。當然,這裡要先 \(dfs\) 子樹再查詢當前點的答案。
問題就解決了,複雜度 \(O(n)\)。
程式碼如下:
#include <cstdio> #include <cstring> #include <cctype> #include <algorithm> #include <iostream> #include <queue> using namespace std; inline int read() { int num = 0 ,f = 1; char c = getchar(); while (!isdigit(c)) f = c == '-' ? -1 : f ,c = getchar(); while (isdigit(c)) num = (num << 1) + (num << 3) + (c ^ 48) ,c = getchar(); return num * f; } const int N = 3e5 + 5 ,M = 6e5 + 5; struct Edge { int to ,next; Edge (int to = 0 ,int next = 0) : to(to) ,next(next) {} }G[M]; int head[N] ,idx; inline void add(int u ,int v) { G[++idx] = Edge(v ,head[u]); head[u] = idx; G[++idx] = Edge(u ,head[v]); head[v] = idx; } int fa[N] ,dep[N] ,siz[N] ,son[N] ,top[N]; inline void dfs1(int now) { siz[now] = 1; dep[now] = dep[fa[now]] + 1; for (int i = head[now]; i ; i = G[i].next) { int v = G[i].to; if (v == fa[now]) continue; fa[v] = now; dfs1(v); siz[now] += siz[v]; if (siz[v] > siz[son[now]]) son[now] = v; } } inline void dfs2(int now ,int t) { top[now] = t; if (!son[now]) return ; dfs2(son[now] ,t); for (int i = head[now]; i ; i = G[i].next) { int v = G[i].to; if (v == fa[now] || v == son[now]) continue; dfs2(v ,v); } } inline int LCA(int x ,int y) { while (top[x] != top[y]) { if (dep[top[x]] < dep[top[y]]) swap(x ,y); x = fa[top[x]]; } return dep[x] < dep[y] ? x : y; } struct Query { int sign ,val; Query(int sign = 0 ,int val = 0) : sign(sign) ,val(val) {} }; vector <Query> q1[N] ,q2[N]; int ans[N] ,t1[M] ,t2[M] ,w[N] ,n ,m; inline void dfs(int now) { int lasta = t1[dep[now] + w[now]] ,lastb = t2[w[now] - dep[now] + n]; for (int i = 0; i < (int)q1[now].size(); i++) { int sign = q1[now][i].sign ,val = q1[now][i].val; t1[val] += sign; } for (int i = 0; i < (int)q2[now].size(); i++) { int sign = q2[now][i].sign ,val = q2[now][i].val; t2[val] += sign; } for (int i = head[now]; i ; i = G[i].next) { int v = G[i].to; if (v == fa[now]) continue; dfs(v); } ans[now] = t1[dep[now] + w[now]] - lasta + t2[w[now] - dep[now] + n] - lastb; } signed main() { n = read() ,m = read(); for (int i = 1; i <= n - 1; i++) { int u = read() ,v = read(); add(u ,v); } for (int i = 1; i <= n; i++) w[i] = read(); dfs1(1); dfs2(1 ,1); for (int i = 1; i <= m; i++) { int u = read() ,v = read(); int Lca = LCA(u ,v); q1[u].push_back(Query(1 ,dep[u])); q1[Lca].push_back(Query(-1 ,dep[u])); q2[v].push_back(Query(1 ,dep[u] - 2 * dep[Lca] + n)); q2[fa[Lca]].push_back(Query(-1 ,dep[u] - 2 * dep[Lca] + n)); } dfs(1); for (int i = 1; i <= n; i++) printf("%d%c" ,ans[i] ," \n"[i == n]); return 0; }