• 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
    ",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

  • 相关阅读:
    shell 读取文件内容 不以空格换行 再把每行的字符串切分取一部分
    Linux中basename和dirname命令的妙用
    Martiancloud 无注册中心微服务
    spring security自动续签功能
    访问c++类的私有成员
    Ubuntu 安装 docker 及 镜像加速
    谷歌浏览器表单自动填充颜色修改(antd)
    关于复制粘贴失效解决办法
    常见的HTTP状态码(HTTP Status Code)
    Linux 环境安装 MySQL
  • 原文地址:https://www.cnblogs.com/nietzsche-oier/p/6604904.html
Copyright © 2020-2023  润新知