POJ P1741 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. 
            --by  POJ
http://poj.org/problem?id=1741


点分治的相关题目;
有关点分治的内容;
题目大意为查询树上最短距离不大于k的点对个数;
我们应用点分治的思路,
统计在完全在某子树内经过其重心的路径;
然后再递归分治下去;
统计方式采取,dfs出子树中,点到重心的距离,查找合法(dis(i)+dis(j)≤k)的组合;
如何查找合法的组合?
1 sort(dis+1,dis+num+1);
2 r=num;
3 for(l=1;l<=num,l<r;l++){
4     while(dis[l]+dis[r]>k&&l<r)
5       r--;
6     ans+=r-l;
7 }

可以看出查找效率主要在排序上,反正很快;

但发现当两点在同一子树中且dis(i)+dis(j)≤k时,他们被计入答案,但他们的最短路径甚至不经过该重心;

于是把这些点用相似的方式查出再减去即可;

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct ss{
    int next,to,w;
};
ss e[20010];
int first[10010],num;
int n,k,ans,size[10010],max_size[10010],vis[10010],dis[10010];
inline void Init();
inline void build(int ,int ,int );
inline void part_dfs(int );
inline void dfs_size(int ,int );
inline int  dfs_root(int ,int ,int );
inline int  chan_ans(int ,int );
inline void dfs_len(int ,int ,int );
inline void in(int &ans)
{
    ans=0;bool p=false;char ch=getchar();
    while((ch>'9' || ch<'0')&&ch!='-') ch=getchar();    if(ch=='-') p=true,ch=getchar();
    while(ch<='9'&&ch>='0') ans=ans*10+ch-'0',ch=getchar();    if(p) ans=-ans;
}
int main()
{
    int i,j,u,v,w;
    while(scanf("%d%d",&n,&k)==2){
        if(n==0)
            return 0;
        Init();
        for(i=1;i<=n-1;i++){
            in(u),in(v),in(w);
            build(u,v,w);build(v,u,w);
        }
        part_dfs(1);
        printf("%d\n",ans);
    }
}
inline void Init(){
    memset(vis,0,sizeof(vis));
    memset(first,0,sizeof(first));
    ans=0;num=0;
}
inline void build(int f,int t,int wi){
    e[++num].next=first[f];first[f]=num;
    e[num].to=t;e[num].w=wi;
}
inline void part_dfs(int now){
    int root,i;
    dfs_size(now,0);
    root=dfs_root(now,0,now);
    ans+=chan_ans(root,0);
    vis[root]=1;
    for(i=first[root];i;i=e[i].next)
        if(!vis[e[i].to]){
            ans-=chan_ans(e[i].to,e[i].w);
            part_dfs(e[i].to);
        }
}
inline void dfs_size(int now,int fa){
    size[now]=1;
    for(int i=first[now];i;i=e[i].next)
        if(!vis[e[i].to]&&e[i].to!=fa){
            dfs_size(e[i].to,now);
            size[now]+=size[e[i].to];
        }
}
inline int dfs_root(int now,int fa,int r){
    int i,root=-1,wroot;
    max_size[now]=size[r]-size[now];
    for(i=first[now];i;i=e[i].next)
        if(!vis[e[i].to]&&e[i].to!=fa){
            if(size[e[i].to]>max_size[now])
                max_size[now]=size[e[i].to];
            wroot=dfs_root(e[i].to,now,r);
            if(max_size[wroot]<max_size[root]||root==-1)
                root=wroot;
        }
    if(max_size[now]<max_size[root]||root==-1)
        root=now;
    return root;
}
inline int chan_ans(int root,int dis1){
    int l,r,ans=0;
    num=0;
    dfs_len(root,dis1,0);
    sort(dis+1,dis+num+1);
    r=num;
    for(l=1;l<=num,l<r;l++){
        while(dis[l]+dis[r]>k&&l<r)r--;
        ans+=r-l;
    }
    return ans;
}
inline void dfs_len(int now,int d,int fa){
    dis[++num]=d;
    for(int i=first[now];i;i=e[i].next)
        if(!vis[e[i].to]&&e[i].to!=fa)
            dfs_len(e[i].to,d+e[i].w,now);
}
//点分治: 
//dfs分治{ 
//对每个分支dfs(两遍)重心
//以重心为根dfs统计合法路径个数
//}分治重心的子树结构 
//dis:一个不具有顺序的点到当前结构的重心的距离表 
//vis[i]标记(染色)已到过的重心 
//size[i]
//max_size[i]记录某点的最大子树大小; 
//由于基于每个分支的dfs之间是跳着的(因为现在的重心未必是前重心的子节点),所以需要vis顺便截断某次dfs,防止跑出分支 

祝AC

 

posted @ 2017-03-23 14:19  F.W.Nietzsche  阅读(268)  评论(0编辑  收藏  举报