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; }