树上点分治 poj 1741

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 的!!!代码示例 :
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

时间: 2024-10-10 10:07:38

树上点分治 poj 1741的相关文章

点分治——POJ 1741

写的第一道点分治的题目,权当认识点分治了. 点分治,就是对每条过某个点的路径进行考虑,若路径不经过此点,则可以对其子树进行考虑. 具体可以看menci的blog:点分治 来看一道例题:POJ 1741 Tree 题目大意:扔给你一颗有权无根树,求有多少条路径的长度小于k: 解题思路:先找出重心,用一次dfs处理出每个点到根的距离dis,然后将dis[]排序,用O(n)的复杂度处理出"过根且长度小于等于k的路径数目",删除根节点,对于每棵子树重复上述操作. 注意要去重: 像上面这样一个图

【点分治】【POJ 1741】【cogs 1714】树上的点对

1714. [POJ1741][男人八题]树上的点对 ★★★ 输入文件:poj1741_tree.in 输出文件:poj1741_tree.out 简单对比 时间限制:1 s 内存限制:256 MB [题目描述] 给一棵有n个节点的树,每条边都有一个长度(小于1001的正整数). 定义dist(u,v)=节点u到节点v的最短路距离. 给出一个整数k,我们称顶点对(u,v)是合法的当且仅当dist(u,v)不大于k. 写一个程序,对于给定的树,计算有多少对顶点对是合法的. [输入格式] 输入包含多

POJ 1741 Tree (树上点分治)(楼教主男人八题之一)

题目地址:POJ 1741 树分治第一发! 树分治详情请看漆子超的国家集训队论文,论文传送门 树分治裸题. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.h> #include <map> #include <set> #

【POJ 1741】 Tree (树的点分治)

Tree Description 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)

POJ - 1987 Distance Statistics 树上的分治

题目大意:和poj 1741的那题和类似,求树上节点之间的距离小于等于k的节点对有多少对 解题思路:具体可参考:<分治算法在树的路径问题中的应用--漆子超> 给这题的输入坑了,注意输入,不然会超时 #include<cstdio> #include<vector> #include<algorithm> using namespace std; #define maxn 40010 int vis[maxn], Sum[maxn], d[maxn], dp[

POJ 1987 Distance Statistics (树上点分治)

题目地址:POJ 1987 点分治模板题,跟POJ 1741几乎一样,.. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.h> #include <map> #include <set> #include <stdi

POJ 1741 树分治(点分治模板题)

POJ 1741 题意:求一棵树中点对<=k的数量. 总结:点分治,搞不太懂..大概敲了一遍 #include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stac

poj 1741 树的点分治(入门)

Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 18205   Accepted: 5951 Description 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

树的点分治 (poj 1741, 1655(树形dp))

poj 1655:http://poj.org/problem?id=1655 题意: 给无根树,  找出以一节点为根,  使节点最多的树,节点最少. 题解:一道树形dp,先dfs 标记 所有节点的子树的节点数. 再dfs  找出以某节点为根的最大子树,节点最少. 复杂度(n) /***Good Luck***/ #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <cs