【树分治】poj1741 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
  • 题目大意
    给定无根树一颗,求满足u到v的距离小于等于k的(u,v)点对个数。

男人八题之一,点分治。

点分治,在一颗无根树中找到树的重心(重心就是树中的一点,满足这一点的所有子节点中size值最大的最小),然后把重心去掉,对于重心的每一个子树进行相同操作。
这道题就是对于每一个子树,统计一下在根节点的不同子树中的两点对个数即可(相同子树中的点对分治到子树中操作即可)
先建图:

struct edge{
    int v,w,next;
}e[MAXM];
void add_edge(int u, int v, int w)  
{  
    e[edge].v = v;  
    e[edge].w = w;  
    e[edge].next = head[u];  
    head[u] = edge++;  
}  

dfs找size:

void dfssize(int u,int fa){
    size[u]=1;
    mx[u]=0;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(v!=fa&&!vis[v]){
            dfssize(v,u);
            size[u]+=size[v];
            if(size[v]>mx[u])
                mx[u]=size[v];
        }
    }
}

然后dfs找root:

void dfsroot(int r,int u,int fa){
    if(size[r]-size[u]>mx[u])
        mx[u]=size[r]-size[u];
    if(mx[u]<minn)
        minn=mx[u],root=u;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(v!=fa&&!vis[v])
            dfsroot(r,v,u);
    }
}

然后再写一个dfs求两点距离:

void dfsdis(int u,int d,int fa){
    dis[num++]=d;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(v!=fa&&!vis[v])
            dfsdis(v,d+e[i].w,u);
    }
}

然后写主dfs分治:

void dfsroot(int r,int u,int fa){
    if(size[r]-size[u]>mx[u])
        mx[u]=size[r]-size[u];
    if(mx[u]<minn)
        minn=mx[u],root=u;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(v!=fa&&!vis[v])
            dfsroot(r,v,u);
    }
}

还有一个是计算刚才说的跨过根节点的点对个数:

int calc(int u,int d){
    int res=0;
    num=0;
    dfsdis(u,d,0);
    sort(dis,dis+num);
    int i=0,j=num-1;
    while(i<j){
        while(dis[i]+dis[j]>k&&i<j) j--;
        res+=j-i;
        i++;
    }
    return res;
}

然后就完事了。。。对于我这个dfs写的很low的人,写四个dfs也是醉了。

直接贴全代码:

#include <iostream>
#include <cstdio> 
#include <vector>
#include <climits>
#include <cstring>
#include <algorithm>
#define MAXN 20000
#define MAXM 50000
using namespace std;
struct edge{
    int v,w,next;
}e[MAXM];
int edge,ans,num,minn;
int size[MAXN],dis[MAXN],mx[MAXN],head[MAXN];
bool vis[MAXN];
int n,k;
int root;
void pre(){
    memset(head,-1,sizeof head);
    memset(vis,0,sizeof vis);
    edge=ans=0;
}
void add_edge(int u, int v, int w)  
{  
    e[edge].v = v;  
    e[edge].w = w;  
    e[edge].next = head[u];  
    head[u] = edge++;  
}  
void dfssize(int u,int fa){
    size[u]=1;
    mx[u]=0;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(v!=fa&&!vis[v]){
            dfssize(v,u);
            size[u]+=size[v];
            if(size[v]>mx[u])
                mx[u]=size[v];
        }
    }
}
void dfsroot(int r,int u,int fa){
    if(size[r]-size[u]>mx[u])
        mx[u]=size[r]-size[u];
    if(mx[u]<minn)
        minn=mx[u],root=u;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(v!=fa&&!vis[v])
            dfsroot(r,v,u);
    }
}
void dfsdis(int u,int d,int fa){
    dis[num++]=d;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(v!=fa&&!vis[v])
            dfsdis(v,d+e[i].w,u);
    }
}
int calc(int u,int d){
    int res=0;
    num=0;
    dfsdis(u,d,0);
    sort(dis,dis+num);
    int i=0,j=num-1;
    while(i<j){
        while(dis[i]+dis[j]>k&&i<j) j--;
        res+=j-i;
        i++;
    }
    return res;
}
void dfs(int u){
    minn=n;
    dfssize(u,0);
    dfsroot(u,u,0);
    ans+=calc(root,0);
    vis[root]=1;
    for(int i=head[root];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(!vis[v]){
            ans-=calc(v,e[i].w);
            dfs(v);
        }
    }
}
int main()  
{  
    while(scanf("%d%d",&n,&k)!=EOF)  
    {  
        if(!n && !k) break;  
        pre();  
        int u, v, w;  
        for(int i=0;i<n-1;i++)  
        {  
            scanf("%d%d%d", &u, &v, &w);  
            add_edge(u, v, w);  
            add_edge(v, u, w);  
        }  
        dfs(1);  
        printf("%d\n", ans);  
    }  
    return 0;  
}  

原po 传送门

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值