日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

hihoCoder #1954 : 压缩树(虚树)

發(fā)布時(shí)間:2025/4/14 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 hihoCoder #1954 : 压缩树(虚树) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

題意

有一棵 \(n\) 個(gè)節(jié)點(diǎn)且以 \(1\) 為根的樹,把它復(fù)制成 \(m\) 個(gè)版本,有 \(q\) 次操作,每次對 \([l, r]\) 這些版本的 \(v\) 節(jié)點(diǎn)到根的路徑收縮起來。

收縮 \(v\) 也就是把 \(v\) 到根路徑上(除了根)所有點(diǎn)的父親都變成根。

最后查詢每個(gè)版本的每個(gè)點(diǎn)的 \(dep\) 之和。

數(shù)據(jù)范圍

\(n, m, q \le 2 \times 10^5\)

題解

操作順序是無所謂的,我們假設(shè)操作了點(diǎn)集 \(S\) ,那么最后被縮上去的點(diǎn)其實(shí)就是 \(\{S, root\}\) 構(gòu)成虛樹經(jīng)過的節(jié)點(diǎn)。

每個(gè)點(diǎn)的深度其實(shí)它原來的深度減去它到根(除了根與根的兒子)被縮的點(diǎn)的個(gè)數(shù)。

考慮祖先對它的貢獻(xiàn)是比較麻煩的,我們不妨考慮它對于祖先的貢獻(xiàn),其實(shí)就是每個(gè)深度 \(\ge 2\) 的節(jié)點(diǎn)的子樹 \(size\) 之和。

那么我們把操作離線,只需要?jiǎng)討B(tài)維護(hù)虛樹經(jīng)過所有點(diǎn)的權(quán)值和。

這其實(shí)是一個(gè)經(jīng)典的動(dòng)態(tài)虛樹的問題,按照 \(dfs\) 序,用 std :: set 維護(hù)當(dāng)前的點(diǎn)集,假設(shè)插入點(diǎn)為 \(k\) 找到它的前驅(qū) \(l\) 與后繼 \(r\) ,令 \(\mathrm{LCA}(l, k), \mathrm{LCA}(r, k)\) 深度較大點(diǎn)為 \(f\) ,那么這次新產(chǎn)生的路徑是 \((k, f)\) 的路徑(注意 \(f\) 原來就是存在于虛樹中的,需要去掉),刪除是類似的。

注意可能一個(gè)點(diǎn)被縮多次,我們需要利用 std :: multiset ,然后每次插入刪除的時(shí)候查找是否還存在即可。

復(fù)雜度是 \(\mathcal O((n + q) \log n + m)\) 的。

代碼

#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 Rep(i, r) for (register int i = (0), i##end = (int)(r); 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) << endlusing namespace std;typedef long long ll; typedef pair<int, int> PII;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_shadowfreopen ("1954.in", "r", stdin);freopen ("1954.out", "w", stdout); #endif }const int N = 2e5 + 1e3;vector<int> G[N];ll ans = 0, sum[N];int n, m, q, dep[N], anc[N][20], Log2[N], sz[N], dfn[N];void Dfs_Init(int u, int fa = 0) {static int clk = 0; dfn[u] = ++ clk;dep[u] = dep[anc[u][0] = fa] + 1;ans += dep[u]; sz[u] = 1;for (int v : G[u]) if (v != fa) Dfs_Init(v, u), sz[u] += sz[v]; }void Get_Sum(int u, int fa = 0) {sum[u] = sum[fa] + (dep[u] > 1) * sz[u];for (int v : G[u]) if (v != fa) Get_Sum(v, u); }struct Cmp {inline bool operator () (const int &lhs, const int &rhs) { return dfn[lhs] < dfn[rhs]; } };vector<int> add[N], del[N]; multiset<int, Cmp> S;inline int Lca(int x, int y) {if (dep[x] < dep[y]) swap(x, y);int gap = dep[x] - dep[y];For (i, 0, Log2[gap]) if (gap >> i & 1) x = anc[x][i];if (x == y) return x;Fordown (i, Log2[dep[x]], 0)if (anc[x][i] != anc[y][i]) x = anc[x][i], y = anc[y][i];return anc[x][0]; }int Find(int x) {PII res; auto it = S.upper_bound(x);if (it != S.end()) {int tmp = Lca(*it, x); chkmax(res, {dep[tmp], tmp});}if (it != S.begin()) {int tmp = Lca(*prev(it), x); chkmax(res, {dep[tmp], tmp});}return res.second ? res.second : x; }int main () {File();n = read(); m = read(); q = read();For (i, 1, n - 1) {int u = read(), v = read();G[u].push_back(v); G[v].push_back(u);}while (q --) {int l = read(), r = read(), v = read();add[l].push_back(v); del[r + 1].push_back(v);}dep[0] = -1; Dfs_Init(1); Get_Sum(1);For (i, 2, n) Log2[i] = Log2[i >> 1] + 1;For (j, 1, Log2[n]) For (i, 1, n)anc[i][j] = anc[anc[i][j - 1]][j - 1];S.insert(1);For (i, 1, m) {for (int x : add[i]) {if (S.find(x) == S.end())ans -= sum[x] - sum[Find(x)];S.insert(x);}for (int x : del[i]) {S.erase(S.find(x));if (S.find(x) == S.end())ans += sum[x] - sum[Find(x)];}printf ("%lld%c", ans, i == iend ? '\n' : ' ');}return 0;}

轉(zhuǎn)載于:https://www.cnblogs.com/zjp-shadow/p/10726212.html

總結(jié)

以上是生活随笔為你收集整理的hihoCoder #1954 : 压缩树(虚树)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。