【树分治】 POJ 1741 Tree

2 篇文章 0 订阅
Tree
Time Limit: 1000MS Memory Limit: 30000K
Total Submissions: 17624 Accepted: 5757

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

Source




这道题的题意大致是说有一棵n个点的树,要我们求树上距离小于k的点对的个数。

首先看数据范围,n<=10000,n^2的做法基本不用去想了。
然后,这是一棵树,那么对于任意一个节点,通过这条线的节点必然在点的两侧。

那么,每一次我们找到一个点,然后把通过这个点的所有直线找出来,那么之后这个点就可以无视掉,然后再在拆开的两棵子树中寻找一个点,以此类推。
在另一篇博客中咱介绍到了树的重心的内容,由树的重心的性质我们可知每次找重心可以达到最优的效果

然后我们找到重心之后,扫描点到其他点的连线长度。
求距离排序一遍O(nlogn)然后左右同时扫一遍O(n),容斥排掉两点在同一子树的情况即可。

代码如下:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int MAXN=20010;
const int MAXM=10010;

int n,m,ans;

int u[MAXN],v[MAXN],w[MAXN],nex[MAXN];
int fir[MAXM];
bool vis[MAXM];
int e_max;

void add_edge(int a,int b,int c)
{
    int e=e_max++;
    u[e]=a;
    v[e]=b;
    w[e]=c;
    nex[e]=fir[a];
    fir[a]=e;
}

int subtree[MAXM];
int maxtree[MAXM];
int pt;

void DfsSon(int x,int father)
{
//    cout<<x<<" "<<father<<endl;
//    system("pause");
    subtree[x]=0;
    maxtree[x]=0;
    for(int e=fir[x];~e;e=nex[e])
    {
        if(vis[v[e]] || v[e]==father) continue;
        DfsSon(v[e],x);
        subtree[x]+=subtree[v[e]]+1;
        maxtree[x]=max(maxtree[x],subtree[v[e]]+1);
    }
}

void DfsSize(int root,int x,int father)
{
    maxtree[x]=max(maxtree[x],subtree[root]-subtree[x]);
    if(maxtree[pt]>maxtree[x])
    {
        pt=x;
    }
    for(int e=fir[x];~e;e=nex[e])
    {
        if(vis[v[e]] || v[e]==father) continue;
        DfsSize(root,v[e],x);
    }
}

int colle[MAXM],cont;

void DfsColle(int x,int father,int lenth)
{
    colle[cont++]=lenth;
    for(int e=fir[x];~e;e=nex[e])
    {
        if(vis[v[e]] || father==v[e]) continue;
        DfsColle(v[e],x,lenth+w[e]);
    }
}

int calc(int x,int lenth)
{
    int tmp=0;
    cont=0;
    //colle[cont++]=0;
    DfsColle(x,0,lenth);
    sort(colle,colle+cont);

//    cout<<"cont="<<cont;
//    for(int i=0;i<cont;i++) printf(",%d",colle[i]);
//    cout<<endl;

    int l=0,r=cont-1;
    while(l<r)
    {
        while(colle[l]+colle[r]>m && l<r) r--;
        tmp+=r-l;
        l++;
    }
    return tmp;
}

void Dfs(int x)
{
    DfsSon(x,0);
    pt=x;
    DfsSize(x,x,0);
    //cout<<"pt="<<pt<<endl;
    ans+=calc(pt,0);
    vis[pt]=1;

    for(int e=fir[pt];~e;e=nex[e])
    {
        if(vis[v[e]]) continue;
        ans-=calc(v[e],w[e]);
        Dfs(v[e]);
    }
//    cout<<ans<<endl;
}

int main()
{
    while(~scanf("%d%d",&n,&m) && n)
    {
        e_max=0;
        memset(fir,-1,sizeof fir);
        memset(vis,0,sizeof vis);
        for(int i=1;i<n;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add_edge(u,v,w);
            add_edge(v,u,w);
        }

        ans=0;
        Dfs(1);
        printf("%d\n",ans);
    }
    return 0;
}






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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值