dsu on treec++
點我跳轉spa
給出一棵樹,每條邊有權。求一條簡單路徑,使得路徑和等於 \(k\),且邊的數量最小。code
問最小數量是多少(若沒有知足條件的則輸出 -1)ci
定義 \(dis_u\) 表示節點 \(u\) 到根節點的距離,\(dep_u\) 表示節點 \(u\) 的深度get
那麼樹上任意兩點 \(u , v\) 的簡單路徑和等於 \(dis_u + dis_v - 2 × dis_{lca(u , v)}\)it
兩點之間邊的數量爲 \(dep_u + dep_v - 2 × dep_{lca(u , v)}\)class
那麼問題就轉換成在樹上找到兩點 $ u , v $ 使得 \(dis_u + dis_v - 2 × dis_{lca(u , v)} = k\) 且 \(dep_u + dep_v - 2 × dep_{lca(u , v)}\) 儘量小map
假設當前根節點爲 \(rt\) ,那麼對於子節點 \(x\) 的來講,能與它構成知足上述式子的 \(y\) 可能來自 \(rt\) 的其它分支,也可能 \(y = rt\)統計
(相同分支內的節點答案的在根節點爲 \(rt\) 的子節點的時候就已經算過了)di
因而咱們能夠定義 \(mp_d\) 表示當前距離\(1\)號點距離爲 \(d\) 的節點的最小深度 , 定義\(c = k+2 * dis_{rt} - dis{x}\)
那麼 \(x\) 提供的貢獻就是 \(mp_c + dep_x - 2 * dep_{rt}\)
由於咱們計算兩點的簡單路徑權值和、兩點簡單路徑的邊的個數是根據它們的\(lca\),因此一個分支內的節點不能相互影響
因此須要先對一個分支統計完貢獻後,再添加它的信息
(事實上 \(rt\) 的任意一個分支內的貢獻在統計子節點爲根的子樹中就已經計算過了,
上文也已經講過了)
#include<bits/stdc++.h> #define rep(i,a,n) for (int i=a;i<=n;i++) #define int long long using namespace std; const int N = 3e5 + 10; struct Edge{ int nex , to , w; }edge[N << 1]; int head[N] , TOT; void add_edge(int u , int v , int w) { edge[++ TOT].nex = head[u] ; edge[TOT].to = v; edge[TOT].w = w; head[u] = TOT; } map<int , int>mp; int n , k , ans = 0x3f3f3f3f; int hson[N] , HH , sz[N] , dep[N] , dis[N]; void dfs(int u , int far) { sz[u] = 1; dep[u] = dep[far] + 1; for(int i = head[u] ; i ; i = edge[i].nex) { int v = edge[i].to , w = edge[i].w; if(v == far) continue ; dis[v] = w + dis[u]; dfs(v , u); sz[u] += sz[v]; if(sz[v] > sz[hson[u]]) hson[u] = v; } } void change(int u , int far) { if(mp.count(dis[u])) mp[dis[u]] = min(mp[dis[u]] , dep[u]); else mp[dis[u]] = dep[u]; for(int i = head[u] ; i ; i = edge[i].nex) { int v = edge[i].to; if(v == far || v == HH) continue ; change(v , u); } } void calc(int u , int far , int rt) { int x = k + 2 * dis[rt] - dis[u]; if(mp.count(x)) ans = min(ans , mp[x] + dep[u] - 2 * dep[rt]); for(int i = head[u] ; i ; i = edge[i].nex) { int v = edge[i].to; if(v == far || v == HH) continue ; calc(v , u , rt); } } void dsu(int u , int far , int op) { for(int i = head[u] ; i ; i = edge[i].nex) { int v = edge[i].to; if(v == far || v == hson[u]) continue ; dsu(v , u , 0); } if(hson[u]) dsu(hson[u] , u , 1) , HH = hson[u]; int x = k + dis[u]; if(mp.count(x)) ans = min(ans , mp[x] + dep[u] - 2 * dep[u]); mp[dis[u]] = dep[u]; for(int i = head[u] ; i ; i = edge[i].nex) { int v = edge[i].to , w = edge[i].w; if(v == far || v == HH) continue ; calc(v , u , u) , change(v , u); } HH = 0; if(!op) mp.clear(); } signed main() { cin >> n >> k; rep(i , 1 , n - 1) { int u , v , w; cin >> u >> v >> w; u ++ , v ++ ; add_edge(u , v , w) , add_edge(v , u , w); } dfs(1 , 0); dsu(1 , 0 , 0); if(ans == 0x3f3f3f3f) cout << -1 << '\n'; else cout << ans << '\n'; return 0; }