//题意:一棵树有边权,询问一条长度为k的简单路径所需的最小步数 // 思路: 点分治,主要是合并的思路,因为是要求最小步数,所以我们对于每一种长度记最小步数即可 // #include <bits/stdc++.h> using namespace std; #define int long long const int N = 2e5 + 10; const int M = 1e6 + 10; vector<pair<int, int>> e[N]; namespace CenDec { int ctr, n, sz[N]; bool del[N]; void dfs(int p, int fa = 0) { sz[p] = 1; int mss = 0; for (auto to : e[p]) { if (del[to.first] || to.first == fa) continue; dfs(to.first, p); if (ctr != -1) return;//在子树递归过程中找到重心就即时退出 mss = max(mss, sz[to.first]); sz[p] += sz[to.first]; } mss = max(mss, n - sz[p]);//与根节点之上的那棵子树进行比较 if (mss <= n / 2) { ctr = p; sz[fa] = n - sz[p];//更新sz[fa]的值,目的是把重心相邻的所有子树大小重新更新一遍,因为待会我们要 //从这个重心向下分治,向下分治的话我们是需要用到相邻子树大小的 } } int k, cnt = 1e9; map<int, int> depp; pair<int, int> temp[2 * N];//第一维记路径长度,第二维记步数 int cntt; void add(int i) { if (depp.find(temp[i].first) == depp.end()) depp[temp[i].first] = temp[i].second; else depp[temp[i].first] = min(depp[temp[i].first], temp[i].second); } void dfs2(int p, int fa, int w, int dp) { if (w > k) return;//剪枝,同时防止数组访问越界 if (depp.find(k - w) != depp.end()) { cnt = min(cnt, depp[k - w] + dp); } temp[++cntt] = { w,dp }; for (auto to : e[p]) if (!del[to.first] && to.first != fa) dfs2(to.first, p, w + to.second, dp + 1); } void run(int p) { depp[0] = 0; for (auto to : e[p]) { if (del[to.first]) continue; dfs2(to.first, p, to.second, 1); for (int i = 1; i <= cntt; ++i) add(i); cntt = 0; } depp.clear();//清空该重心的统计 del[p] = 1; for (auto to : e[p]) { if (!del[to.first]) { n = sz[to.first];//现在要遍历的树是上个重心的子树 ctr = -1; dfs(to.first); run(ctr); } } } int count(int n0, int k0) { n = n0, k = k0; ctr = -1; dfs(1);//找重心 run(ctr);//计算贡献 if (cnt != 1e9) return cnt; else return -1; } } signed main() { int n, k; cin >> n >> k; for (int i = 1; i <= n - 1; i++) { int a, b, c; cin >> a >> b >> c; a++, b++; e[a].push_back({ b,c }); e[b].push_back({ a,c }); } cout << CenDec::count(n, k) << endl; return 0; }
标签:sz,temp,int,分治,mss,Race,depp,写法,first From: https://www.cnblogs.com/Aacaod/p/17030245.html