POJ 1741 Tree + POJ 1987 Distance Statistics【树的点分治】

2 篇文章 0 订阅
1 篇文章 0 订阅

Tree
Time Limit: 1000MS Memory Limit: 30000K
Total Submissions: 13558 Accepted: 4367
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。
思路:参考http://blog.sina.com.cn/s/blog_6d5aa19a0100o73m.html
主要就是掌握对树进行分治来降低复杂度的思想,将一颗无根树化成有根树,处理完以原树之后,就进入到原树的子树中进行处理,即为分治。在这道题里需要注意处理的是数对在根节点的一颗子树里和不在一颗子树里的情况,注意相减关系。
详见代码:

#pragma warning(disable:4996)
#include<stdio.h>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
#define maxn 100006
#define inf 0x7fffffff
struct node
{
    int v,c;
    node(){}
    node(int v,int c):v(v),c(c){}
};
int n,m,K;
vector<node>g[maxn];
int size[maxn],Mson[maxn],root,deep[maxn],d[maxn];
bool vis[maxn];
int ans,sum;
//getRoot 得到以root为根的树的重心,size[i]表示以i为根的树的大小,Mson表示i的最大的子树
void getRoot(int x,int p = 0){
    size[x] = 1;
    Mson[x] = 0;
    int length = g[x].size();
    for (int i = 0; i < length; i++)
    {
        int v = g[x][i].v;
        if(vis[v] == 0 && v != p){
            getRoot(v,x);
            size[x] += size[v];
            Mson[x] = max(Mson[x],size[v]);
        }
    }
    Mson[x] = max(Mson[x],sum-size[x]);
    if(Mson[x] < Mson[root]) root = x;
}
//getDeep 得到子节点到根节点的deep数组
void getDeep(int x,int p = 0){
    deep[++deep[0]] = d[x];
    for (int i = 0;i<g[x].size();i++)
    {
        int v = g[x][i].v;
        if(vis[v]==0 && v != p){
            d[v] = d[x] + g[x][i].c;
            getDeep(v,x);
        }
    }
}
// cal 计算以x为根的所有子树里满足条件的点对,注意在后续需要减去在同一棵子树里的点对
int cal(int x,int dis = 0){
    d[x] = dis;
    deep[0] = 0;
    getDeep(x,0);
    sort(deep+1,deep+1+deep[0]);
    int cnt = 0,l,r;
    for (l = 1,r = deep[0];l<r;)
    {
        if(deep[l] + deep[r] <= K)cnt += r-l,l++;
        else r--;
    }
    return cnt;
}
void work(int x){
    ans += cal(x);
    vis[x] = 1;
    for (int i = 0; i < g[x].size(); i++)
    {
        int v = g[x][i].v;
        if(vis[v])continue;
        // 在标记完父亲节点(root)之后,就需要减去root的同一棵子树中的答案
        ans -= cal(v,g[x][i].c);
        sum = size[v];
        root = 0;
        getRoot(v,root);
        work(root);
    }
}
int main(){
    while (cin>>n>>m && n+m)
    {
        int u,v,c;
        //char s[5];
        for (int i = 0; i < n-1; i++)
        {
            //scanf("%d%d%d%s",&u,&v,&c,s);
            scanf("%d%d%d",&u,&v,&c);
            g[u].push_back(node(v,c));
            g[v].push_back(node(u,c));
        }
        //scanf("%d",&K);
        K = m;
        memset(vis,0,sizeof vis);
        ans = 0;    sum = n;
        Mson[0] = inf;
        getRoot(1);
        work(root);
        printf("%d\n",ans);
        for (int i = 1;i<=n;i++)g[i].clear();
    }
    return 0;
}

对于1987,买一送一,改改输入就可以了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值