POJ-1741 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) 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

题意

求路径长度不超过k的路径数

题解

点分治(淀粉质)入门题

通过这篇文章已经简单对点分治入门了(https://blog.csdn.net/qq_39553725/article/details/77542223),知道了为什么要减去子树的答案,以及参数w的使用,这个题就是点分治的模板题了

首先我们对于这种统计树上路径数量,能否找到一条路径的题,可以考虑点分治,因为儿子的信息可能对父亲结点没什么用,对于那种需要维护子节点的信息合并到父节点,需要用儿子的信息算出父节点的答案的题,我们可以用启发式合并或者是dsu on tree

像这个题,直接套用点分治的板子,统计答案的时候先排序再two pointer扫一遍即可。

代码

#include <algorithm>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
typedef long long ll;
struct node {
    int v; ll w;
    node() {}
    node(int v, ll w): v(v), w(w) {}
};
const int N = 1e5 + 50;
const int inf = 1e9;
vector<node> G[N];
int sze[N];
int msze[N];
int S;
int maxx;
int vis[N];
int root;
void getroot(int u, int f) {
    sze[u] = 1;
    msze[u] = 0;
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i].v;
        if (vis[v] || v == f) continue;
        getroot(v, u);
        sze[u] += sze[v];
        msze[u] = max(msze[u], sze[v]);
    }
    msze[u] = max(msze[u], S - sze[u]);
    if (msze[u] < maxx) {
        maxx = msze[u];
        root = u;
    }
}
ll dis[N];
int cnt = 0;
ll k;
void getdis(int u, int f, ll d) {
    dis[++cnt] = d;
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i].v;
        if (vis[v] || v == f) continue;
        getdis(v, u, d + G[u][i].w);
    }
}
ll calc(int u, ll w) {
    cnt = 0;
    getdis(u, 0, w);
    sort(dis + 1, dis + cnt + 1);
    int l = 1, r = cnt;
    ll ans = 0;
    while (l <= r) {
        if (dis[l] + dis[r] <= k) {
            ans += r - l;
            l++;
        }
        else r--;
    }
    return ans;
}

ll ans;
void dfs(int u) {
    ans += calc(u, 0);
    vis[u] = 1;
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i].v;
        if (vis[v]) continue;
        ans -= calc(v, G[u][i].w);
        S = sze[v];
        root = 0;
        maxx = inf;
        getroot(v, 0);
        dfs(root);
    }
}
int main() {
    int n;
    while (scanf("%d%lld", &n, &k)) {
        if (n == 0 && k == 0) break;
        for (int i = 1; i <= n; i++) G[i].clear();
        for (int i = 1; i < n; i++) {
            int u, v; ll w;
            scanf("%d%d%lld", &u, &v, &w);
            G[u].push_back(node(v, w));
            G[v].push_back(node(u, w));
        }
        memset(vis, 0, sizeof(vis));
        cnt = 0;
        S = n;
        root = 0;
        maxx = inf;
        ans = 0;
        getroot(1, 0);
        dfs(root);
        printf("%lld\n", ans);
    }
    return 0;
}

多组数据注意要清空的有:

  1. vis数组
  2. 答案ans
  3. 树的vector
posted @ 2020-01-17 16:25  Artoriax  阅读(195)  评论(0编辑  收藏  举报