树上点分治 poj 1741

本文介绍了一道经典的树上点分治问题,通过寻找树的重心并递归解决子问题来统计距离小于等于给定值的所有点对数量。文章详细展示了算法实现过程及代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Give a tree with n vertices,each edge has a length(positive integer less than 1001).
Define dist(u,v)=The min distance between node u and v.
Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k.
Write a program that will count how many pairs which are valid for a given tree.
Input
The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l.
The last test case is followed by two zeros.
Output
For each test case output the answer on a single line.
Sample Input
5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0
Sample Output
8
题目分析 : 给定一棵树,以及树上边的关系大小,问你又多少对点的距离是小于等于所给定的 d 的
思路分析 : 树上点分治的板子题,首先寻找树的重心,以重心为根结点,寻求所有符合题意要求的点对,但是这样计算会算出一些不符合题目的点对,在减去即可,此时当遍历到一个新的结点时,此时的情况又可以当成最初的情况,找重心的时候要注意,对它的子树来说,总的结点数是小于 n 的!!!
      最后的复杂度是n logn logn    其中每次快排是nlogn 而递归的深度为logn
代码示例 :
const int maxn = 1e4+5;
const int inf = 0x3f3f3f3f;
#define ll long long

int n, m;
struct node
{
    int to, cost;
    node(int _to = 0, int _cost = 0):to(_to), cost(_cost){}
};
vector<node>ve[maxn];
int root;
int size[maxn], mx[maxn]; // size表示每个结点所连的结点数, mx表示对每个根结点所连的最大结点子树有多少的结点
int balance;
bool done[maxn];
int ans = 0;
int numm; // 表示结点总数

void getroot(int x, int fa){
    size[x] = 1, mx[x] = 0;

    for(int i = 0; i < ve[x].size(); i++){
        int to = ve[x][i].to;
        if (to == fa || done[to]) continue;
        getroot(to, x);
        size[x] += size[to];  
        mx[x] = max(mx[x], size[to]);
    }    
    mx[x] = max(mx[x], numm-size[x]); // 对子树在寻找子树的重心的过程中,子树的总结点数是会变小的
    if (mx[x] < balance) {balance = mx[x], root = x;}
}

int cnt = 0;
int dep[maxn];
void dfssize(int x, int fa, int d){
    dep[cnt++] = d;
    
    for(int i = 0; i < ve[x].size(); i++){
        int to = ve[x][i].to;
        int cost = ve[x][i].cost;
        if (to == fa || done[to]) continue;
        dfssize(to, x, d+cost); 
    }
}

int cal(int x, int d){
    cnt = 0;
    dfssize(x, x, d);
    sort(dep, dep+cnt);
    int l = 0, r = cnt-1;
    int sum = 0;
    
    while(l < r){
        if (dep[l]+dep[r] <= m){
            sum += r-l;
            l++;        
        }
        else r--;
    }
    //printf("sum = %d \n", sum);
    //system("pause");
    return sum;
}

void dfs(int x){
    done[x] = true;
    ans += cal(x, 0);
     
    for(int i = 0; i < ve[x].size(); i++){
        int to = ve[x][i].to;
        int cost = ve[x][i].cost;
        
        if (done[to]) continue;
        ans -= cal(to, cost);
        balance = inf;
        numm = size[to]; // 这里是重点,因为这个地方一直T,还以为写的代码有问题
        getroot(to, to);
        //printf("root = %d\n", root);
        dfs(root);
    }
}

int a, b, w;
int main() {
    while(scanf("%d%d", &n, &m) && n+m){
        for(int i = 0; i <= 10000; i++) ve[i].clear();
        memset(done, false, sizeof(done));
        for(int i = 1; i < n; i++){
            scanf("%d%d%d", &a, &b, &w);
            ve[a].push_back(node(b, w));
            ve[b].push_back(node(a, w));            
        }
        ans = 0;
        balance = inf;
        numm = n;
        getroot(1, 1);
        //printf("root = %d\n", root);         
        dfs(root);
        printf("%d\n", ans);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/ccut-ry/p/8481441.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值