1 条题解

  • 0
    @ 2026-3-15 20:03:34
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    
    const int MAXN = 10010;
    int N, K;
    vector<pair<int, int>> graph[MAXN];
    bool deleted[MAXN];
    int sz[MAXN];
    
    // 计算子树大小
    int calcSize(int u, int p) {
        sz[u] = 1;
        for (auto &e : graph[u]) {
            int v = e.first;
            if (v == p || deleted[v]) continue;
            sz[u] += calcSize(v, u);
        }
        return sz[u];
    }
    
    // 找重心
    void findCentroid(int u, int p, int tot, int &centroid, int &best) {
        int maxSub = 0;
        for (auto &e : graph[u]) {
            int v = e.first;
            if (v == p || deleted[v]) continue;
            findCentroid(v, u, tot, centroid, best);
            maxSub = max(maxSub, sz[v]);
        }
        maxSub = max(maxSub, tot - sz[u]);
        if (maxSub < best) {
            best = maxSub;
            centroid = u;
        }
    }
    
    // 收集距离
    void collectDist(int u, int p, int dist, vector<int> &ds) {
        ds.push_back(dist);
        for (auto &e : graph[u]) {
            int v = e.first;
            int w = e.second;
            if (v == p || deleted[v]) continue;
            collectDist(v, u, dist + w, ds);
        }
    }
    
    // 计算距离数组中有多少对 (i<j) 满足 d[i]+d[j] <= K
    int countPairs(vector<int> &ds) {
        sort(ds.begin(), ds.end());
        int cnt = 0;
        int j = (int)ds.size() - 1;
        for (int i = 0; i < ds.size(); ++i) {
            while (j > i && ds[i] + ds[j] > K) --j;
            if (j > i) cnt += (j - i);
            else break;
        }
        return cnt;
    }
    
    // 点分治主函数
    int solve(int u) {
        // 计算当前连通块的大小
        calcSize(u, -1);
        int centroid = u;
        int best = MAXN;
        // 寻找重心
        findCentroid(u, -1, sz[u], centroid, best);
        deleted[centroid] = true;
        int ans = 0;
        // 存储所有节点到重心的距离
        vector<int> allDist;
        allDist.push_back(0); // 重心自身距离为0
        // 处理每个子树
        for (auto &e : graph[centroid]) {
            int v = e.first;
            int w = e.second;
            if (deleted[v]) continue;
            vector<int> subDist;
            collectDist(v, centroid, w, subDist);
            // 减去同一子树内组合的路径
            ans -= countPairs(subDist);
            // 合并到总距离列表
            allDist.insert(allDist.end(), subDist.begin(), subDist.end());
        }
        // 加上所有组合的路径
        ans += countPairs(allDist);
        // 递归处理各子树
        for (auto &e : graph[centroid]) {
            int v = e.first;
            if (!deleted[v]) {
                ans += solve(v);
            }
        }
        return ans;
    }
    
    int main() {
        while (scanf("%d%d", &N, &K) == 2) {
            if (N == 0 && K == 0) break;
            // 初始化
            for (int i = 0; i < N; ++i) {
                graph[i].clear();
                deleted[i] = false;
            }
            // 读入树
            for (int i = 0; i < N - 1; ++i) {
                int u, v, l;
                scanf("%d%d%d", &u, &v, &l);
                graph[u].push_back({v, l});
                graph[v].push_back({u, l});
            }
            int ans = solve(0);
            printf("%d\n", ans);
        }
        return 0;
    }
    

    信息

    ID
    163
    时间
    1000ms
    内存
    128MiB
    难度
    10
    标签
    递交数
    3
    已通过
    1
    上传者