zoukankan      html  css  js  c++  java
  • poj 1741 Tree (树的分治)

    Tree
    Time Limit: 1000MS   Memory Limit: 30000K
    Total Submissions: 30928   Accepted: 10351

    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

    题目大意:

    给定一棵n元树,求有多少点对使得这两点的距离小于等于k。

    树分治经典题。

    以下删改自sdj222555的CSDN博客。

    需要分治。可以看09年漆子超的论文。本题用到的是关于点的分治。

    一个重要的问题是,为了防止退化,所以每次都要找到树的重心然后分治下去,所谓重心,就是删掉此结点后,剩下的结点最多的树结点个数最小。

    每次分治,我们首先算出重心,为了计算重心,需要进行两次dfs,第一次把以每个结点为根的子树大小求出来,第二次是从这些结点中找重心。

    当然在一次dfs过程中也能做到。另外虽然说不记忆化也能做到,但还是用个son数组记忆下吧。

    另外,无向图的dfs不需要像我之前一样遇一个点打个vis标记,dfs定义成dfs(u,pa),保存该点的父亲就不会返回去啦。

    找到重心后,需要统计所有结点到重心的距离,看其中有多少对小于等于K。

    这里采用的方法就是把所有的距离存在一个数组里(不要忘了重心到自己的),进行快速排序,这是nlogn的,然后用一个经典的相向搜索O(n)时间内解决。

    但是这些求出来满足小于等于K的里面只有那些路径经过重心的点对才是有效的,也就是说在同一颗子树上的肯定不算数的。所以对每颗子树,把子树内部的满足条件的点对减去。

    最后的复杂度是n logn logn    其中每次快排是nlogn 而递归的深度为logn。

    代码实现比较复杂,今后有时间还是可以再敲一次。

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #include <cmath>
    #include <queue>
    #include <deque>
    #include <stack>
    #include <map>
    #include <set>
    typedef long long ll;
    const int maxn=10000;
    const int inf=1000000000;
    
    int n,k;
    
    int to[maxn*2+5];
    int w[maxn*2+5];
    int next[maxn*2+5];
    int head[maxn+5];
    int cnt;
    
    void addedge(int a,int b,int c)
    {
        to[cnt]=b;w[cnt]=c;
        next[cnt]=head[a];head[a]=cnt++;
    }
    
    int ans;
    int mins,root;
    int son[maxn+5];
    int vis[maxn+5];
    int depth[maxn+5];
    int dis[maxn+5],tot;
    
    void getroot(int u,int pa,int num)//求重心
    {
        int maxs=0;
        son[u]=1;
        for(int i=head[u];i!=-1;i=next[i])
        {
            int l=to[i];
            if(l!=pa&&!vis[l])
            {
                getroot(l,u,num);
                maxs=std::max(maxs,son[l]);
                son[u]+=son[l];
            }
        }
        maxs=std::max(maxs,num-son[u]);
        if(maxs<mins)
        {
            mins=maxs;
            root=u;
        }
    }
    
    void getdepth(int u,int pa)//通过深度得出需要的dis值
    {
        for(int i=head[u];i!=-1;i=next[i])
        {
            int l=to[i];
            if(l!=pa&&!vis[l])
            {
                depth[l]=depth[u]+w[i];
                dis[tot++]=depth[l];
                getdepth(l,u);
            }
        }
    }
    
    int calc(int u,int pa,int d)
    {
        depth[u]=0;
        tot=0;
        dis[tot++]=0;
        getdepth(u,pa);
        int ret=0;
        std::sort(dis,dis+tot);
        int i=0,j=tot-1;
        while(i<j)//经典的相向搜索
        {
            while(dis[i]+dis[j]+d*2>k&&i<j)
                j--;
            ret+=j-i;
            i++;
        }
        return ret;
    }
    
    void dfs(int x,int num)
    {
        mins=inf;
        getroot(x,-1,num);//找重心
        ans+=calc(root,-1,0);//计算整棵树符合条件的对数
        for(int i=head[root];i!=-1;i=next[i])
        {
            int l=to[i];
            if(!vis[l])
                ans-=calc(l,root,w[i]);//减去每棵子树符合条件的对数
        }
        vis[root]=1;//打标记
        for(int i=head[root];i!=-1;i=next[i])
        {
            int l=to[i];
            if(!vis[l])
                dfs(l,son[l]);//向子树递归
        }
    }
    
    int main()
    {
        while(scanf("%d%d",&n,&k),n||k)
        {
            memset(head,-1,sizeof(head));
            cnt=0;
            for(int i=1,a,b,c;i<=n-1;i++)
            {
                scanf("%d%d%d",&a,&b,&c);
                addedge(a,b,c);
                addedge(b,a,c);
            }
    
            memset(vis,0,sizeof(vis));
            ans=0;
            dfs(1,n);
    
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    VMware Workstation 15 安装教程
    Kail更新源、输入法、浏览器
    Kali Linux 下载、引导、安装
    dwr超时
    jsp获取web的跟路径
    java线程安全
    jsp快速回顾
    在web.xml中可以设置jsp标签吗?
    axis2--生成的wsdl文件方法的参数问题
    java删除文件
  • 原文地址:https://www.cnblogs.com/acboyty/p/9985823.html
Copyright © 2011-2022 走看看