Files
Data-Structure/Algorithm/DivideOnTree/staticDivide.cpp
2026-02-08 16:39:45 +08:00

110 lines
3.0 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 建议使用 N 定义数组,比 vector 稍快且容易管理
const int N = 100005;
vector<pair<int, int>> adj[N];
bool vis[N]; // 分治标记:记录哪些点已经作为重心被“删掉”了
int treesize[N];
int root, min_maxpart, K;
long long ans = 0;
// 存储当前子树所有节点到重心的距离
vector<int> dists;
// 1. 找重心:注意这里不再需要 visited 数组,用 fa 限制方向即可
void get_root(int u, int fa, int currsize) {
treesize[u] = 1;
int maxpart = 0;
for (auto& edge : adj[u]) {
int v = edge.first;
if (v == fa || vis[v]) continue; // 关键:不能走已经“删掉”的点
get_root(v, u, currsize);
treesize[u] += treesize[v];
maxpart = max(maxpart, treesize[v]);
}
maxpart = max(maxpart, currsize - treesize[u]);
if (maxpart < min_maxpart) {
min_maxpart = maxpart;
root = u;
}
}
// 2. 收集距离:把所有节点到重心的距离存入 dists 数组
void get_dists(int u, int fa, int d) {
dists.push_back(d);
for (auto& edge : adj[u]) {
int v = edge.first;
int w = edge.second;
if (v == fa || vis[v]) continue;
get_dists(v, u, d + w);
}
}
// 3. 计算贡献:双指针法统计满足 dist1 + dist2 <= K 的对数
long long calc(int u, int init_dist) {
dists.clear();
get_dists(u, -1, init_dist);
sort(dists.begin(), dists.end());
long long count = 0;
int l = 0, r = dists.size() - 1;
while (l < r) {
if (dists[l] + dists[r] <= K) {
count += (r - l);
l++;
} else {
r--;
}
}
return count;
}
// 4. 点分治主函数
void divide(int u, int currsize) {
// 每次进入先找当前连通块的重心
min_maxpart = currsize + 7; // 初始化为一个大值
get_root(u, -1, currsize);
int r = root; // 锁定重心
vis[r] = true; // 【关键】逻辑切断重心
// 统计经过重心的路径
ans += calc(r, 0);
// 递归子树
for (auto& edge : adj[r]) {
int v = edge.first;
int w = edge.second;
if (vis[v]) continue;
// 【关键】去重:减去在同一个儿子子树内提前满足条件的路径
ans -= calc(v, w);
// 这里的 currsize 要更新为子树的实际大小
// 注意:由于已经跑过 get_roottreesize[v] 此时就是正确的
// 但如果 v 是 r 的“上方”节点size 应该是 currsize - treesize[r]
int next_size = (treesize[v] < treesize[r]) ? treesize[v] : (currsize - treesize[r]);
divide(v, next_size);
}
}
int main() {
int n;
if (!(cin >> n >> K)) return 0;
for (int i = 0; i < n - 1; i++) {
int u, v, w;
cin >> u >> v >> w;
adj[u].push_back({v, w});
adj[v].push_back({u, w});
}
divide(1, n); // 从节点1开始分治总大小为 n
cout << ans << endl;
return 0;
}