• 树链剖分-点的分治(dis[i]+dis[j]<=k的点对数量)


    poj1741

    Tree
    Time Limit: 1000MS   Memory Limit: 30000K
    Total Submissions: 10012   Accepted: 3024

    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个顶点的树形图,找出<u,v>路径的距离不超过k的值,问存在多少对这样的点对(认为<u,v>和<v,u>是一种情况,没有<u,u,>的情况)

    分析:

    因为n<=10000,暴搜一定会超时,所以很明显用的是树的点分治进行处理:点分治即为把树进行递归,分别对每个子树进行操作,然后把每个子树的情况综合起来,对于这道题目,首先找到树根(即树的重心),对于该树,统计dis[i]+dis[j]<=k的数量,

    将无根树转化成有根树进行观察。满足条件的点对有两种情况:两个点的路径横跨树根,两个点位于同一颗子树中。

    如果我们已经知道了此时所有点到根的距离a[i],a[x] + a[y] <= k的(x, y)对数就是结果,这个可以通过排序之后O(n)的复杂度求出。然后根据分治的思想,分别对所有的儿子求一遍即可,但是这会出现重复的——当前情况下两个点位于一颗子树中,那么应该将其减掉(显然这两个点是满足题意的,为什么减掉呢?因为在对子树进行求解的时候,会重新计算)。

    在进行分治时,为了避免树退化成一条链而导致时间复杂度变为O(N^2),每次都找树的重心,这样,所有的子树规模就会变的很小了。时间复杂度O(Nlog^2N)。

    树的重心的算法可以线性求解。

    程序:

    #include"string.h"
    #include"stdio.h"
    #include"stdlib.h"
    #include"queue"
    #include"stack"
    #include"iostream"
    #include"algorithm"
    #include"vector"
    #define inf 999999999
    #define M 11111
    using namespace std;
    struct node
    {
        int u,v,w,next;
    }edge[M*3];
    int t,head[M],use[M],dis[M],son[M],limit[M],k,cnt,MN,ID;
    void init()
    {
        t=0;
        memset(head,-1,sizeof(head));
    }
    void add(int u,int v,int w)
    {
        edge[t].u=u;
        edge[t].v=v;
        edge[t].w=w;
        edge[t].next=head[u];
        head[u]=t++;
    }
    void dfs_size(int u,int f)
    {
        son[u]=1;
        limit[u]=0;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(f!=v&&!use[v])
            {
                dfs_size(v,u);
                son[u]+=son[v];
                limit[u]=max(limit[u],son[v]);
            }
        }
    }
    void dfs_root(int root,int u,int f)
    {
        if(son[root]-son[u]>limit[u])
            limit[u]=son[root]-son[u];
        if(MN>limit[u])
        {
            MN=limit[u];
            ID=u;
        }
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(f!=v&&!use[v])
            {
                dfs_root(root,v,u);
            }
        }
    }//以上两个深搜是找树的重心,记录到ID中
    void dfs_dis(int u,int f,int id)
    {
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(f!=v&&!use[v])
            {
                dfs_dis(v,u,id+edge[i].w);
            }
        }
        dis[cnt++]=id;
    }//记录子树当前根到其他点的距离
    int cal(int u,int f,int id)
    {
        cnt=0;
        int sum=0,i,j;
        dfs_dis(u,f,id);
        sort(dis,dis+cnt);
        for(i=0,j=cnt-1;i<cnt;i++)
        {
            while(i<j&&dis[i]+dis[j]>k)
                j--;
            if(j<=i)
                break;
            sum+=j-i;
        }
        return sum;
    }
    int dfs_ans(int root,int u,int f)
    {
        int sum;
        dfs_size(u,f);
        MN=inf;
        dfs_root(root,u,f);
        sum=cal(ID,ID,0);//先搜根节点到孩子的距离,且求出有多少点对距离小于等于K
        use[ID]=1;
        for(int i=head[ID];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(!use[v])
            {
                sum-=cal(v,v,edge[i].w);//从根节点开始搜的话,会出现子树会搜到的重复的情况,应该减去
                sum+=dfs_ans(v,v,v);
            }
        }
        return sum;
    }
    int main()
    {
        int n,i;
        while(scanf("%d%d",&n,&k),k||n)
        {
            init();
            for(i=1;i<n;i++)
            {
                int a,b,c;
                scanf("%d%d%d",&a,&b,&c);
                add(a,b,c);
                add(b,a,c);
            }
            memset(use,0,sizeof(use));
            int ans=dfs_ans(1,1,1);
            printf("%d
    ",ans);
        }
    }
    





  • 相关阅读:
    物理机安装Centos7
    rust网站
    windows10安装ubuntu双系统教程
    在win10+Ubuntu双系统下,完美卸载Ubuntu
    C# Lambda 学习笔记
    git unable to resolve reference 'refs/remotes/gitee/20210825-add-Projet-coreScene': reference broken
    git如何获取远端仓库新分支
    编码规范
    新萝卜家园GHOST WIN7系统32,64位官方版下载
    Semaphore实战
  • 原文地址:https://www.cnblogs.com/mypsq/p/4348206.html
Copyright © 2020-2023  润新知