//题意:询问一棵树上长度不超过k的简单路径有多少条 // 思路:貌似可以用dsu on tree 但好像要用到平衡树之类的,之后再看看 // https://tqltqltqlorzorzorz.blog.luogu.org/p4178-tree-ti-xie // 在这里用的是树分治做法,分治过程不多说,主要是合并过程 // 我自己的写法是直接合并,每递归一层就利用二分算贡献,所以复杂度应该是达到了O(nlogn*logn),结果T了很多点 // // 对于这种题型可以再优化,利用容斥原理,本来为了保证一棵子树不对自己做贡献,都是做完这棵子树后再将这棵子树加入集合统计 // 但其实可以这样思考 总贡献=将所有情况加入的贡献-每棵子树内部自己的贡献 这样就不用在每层递归都二分一遍了,大大加快速度 // #include <bits/stdc++.h> using namespace std; #define int long long const int N = 50005; 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 = 0; //注释掉的就是容斥写法,没注释掉的是自己的写法 /* vector<int> d1, d2; void dfs2(int x, int fa, int w) { if(w>k) return ; d1.push_back(w); d2.push_back(w); for (auto y : e[x]) { if (del[y.first] || y.first == fa) continue; dfs2(y.first, x, w + y.second); } } int calc(vector<int>& d) { sort(d.begin(), d.end()); int m = d.size(); int r = m - 1, ans = 0; for (int i = 0; i < m; i++) { while (r >= 0 && d[i] + d[r] > k) --r; if (i < r) ans += r - i; } return ans; }*/ int d1[N], d2[N], cnt1, cnt2;//已知长度,暂存长度 void dfs2(int x, int fa, int w) { if (w > k) return; d2[++cnt2] = w; sort(d1, d1 + cnt1 + 1); int ned = k - w; int temp = lower_bound(d1, d1 + cnt1 + 1, ned) - d1; cnt += temp; for (auto y : e[x]) { if (del[y.first] || y.first == fa) continue; dfs2(y.first, x, w + y.second); } } void run(int p) { /*d1.push_back(0); for (auto y : e[p]) { if (del[y.first]) continue; d2.clear(); dfs2(y.first, p, y.second); cnt -= calc(d2); } cnt += calc(d1); d1.clear();*/ d1[0] = 0; for (auto x : e[p]) { if (del[x.first]) continue; dfs2(x.first, p, x.second); for (int i = 1; i <= cnt2; i++) d1[++cnt1] = d2[i]; cnt2 = 0; } cnt1 = 0; 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);//计算贡献 return cnt; } } signed main() { int n, k, u, v, w; cin >> n; for (int i = 1; i <= n - 1; ++i) { cin >> u >> v >> w; e[u].push_back({ v,w }); e[v].push_back({ u,w }); } cin >> k; cout << CenDec::count(n, k) << endl; return 0; }
标签:int,mss,分治,Tree,dfs2,first,d2,d1 From: https://www.cnblogs.com/Aacaod/p/17031154.html