树链剖分-点的分治(点数为k且距离最长的点对)

hdu4871

Shortest-path tree

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 130712/130712 K (Java/Others)
Total Submission(s): 382    Accepted Submission(s): 115


Problem Description
Given a connected, undirected graph G, a shortest-path tree rooted at vertex v is a spanning tree T of G, such that the path distance from root v to any other vertex u in T is the shortest path distance from v to u in G.
We may construct a shortest-path tree using the following method:
We consider a shortest-path tree rooted at node 1. For every node i in the graph G, we choose a shortest path from root to i. If there are many shortest paths from root to i, we choose the one that the sequence of passing nodes' number is lexicographically minimum. All edges on the paths that we chose form a shortest-path tree.
Now we want to know how long are the longest simple paths which contain K nodes in the shortest-path tree and how many these paths? Two simple paths are different if the sets of nodes they go through are different.
 

Input
The first line has a number T (T <= 10), indicating the number of test cases.
For each test case, the first line contains three integers n, m, k(1<=n<=30000,1<=m<=60000,2<=k<=n), denote the number of nodes, the number of edges and the nodes of required paths.
Then next m lines, each lines contains three integers a, b, c(1<=a, b<=n, 1<=c<=10000),denote there is an edge between a, b and length is c.
 

Output
For each case, output two numbers, denote the length of required paths and the numbers of required paths.
 

Sample Input
  
    
1 6 6 4 1 2 1 2 3 1 3 4 1 2 5 1 3 6 1 5 6 1
 

Sample Output
  
    
3 4
题意:

题意其实就是给你一个图,然后让你转换成一棵树,这棵树满足的是根节点1到其余各点的间隔都是图里的最短间隔,并且为了包管这棵树的独一性,路径也必须是最小的。转化成树的办法其实就是跑一次spfa。spfa的时辰记下所有到这个的前驱的边,然后这些边集反向的边补上就是构成所有最短路的边。然后在这些边上跑一次dfs,跑前将边遵守达到点的序号由小到大排序,重视dfs搜的下一个点的间隔必须是最短的才搜,不然的话搜出来的图就是不合错误的。


至此图的项目组转化完了,剩下的就是求一个图里包含了k个点的路径的最长间隔,以及有几许条,类似的题目还有有几许条路径的乘积=k,有几许条路径的和>k,有几许条路径的乘积是完全立方数。。。做法就是典范的树分治。


具体的做法是找出重心,对重心外的项目组递归求解,归并的时辰列举到重心的所有路径,列举的时辰可以用一个全局的map[s].k和map[s].dis分别记录不包含根节点的顶点个数是s的链的个数和该链的最长距离,时刻更新,然后用一个去列举新的项目组的路径,然后经由过程与mp存的信息对比更新答案;

网络赛的解题报告:

首先构造最短路径树。先求根节点到其他节点的最短路径,然后从根节点开始进行深度优先遍历,先遍历节点编号较小的没遍历过的儿子,这样就能处理处最短路径树。

之后找节点数为K的树链。可以用树分治进行求解,在树分治求解过程中,对于每个中心点,处理出该子树中所有节点到中心点的树链,然后枚举每条树链,比如某条树链节点为a,长度为b,则找之前遍历过的树链中节点数位K-a的长度最长的树链,将这两条树链拼起来可以得到节点数为K的树链,用其更新答案。最好一个一个分支分别处理,可以避免考虑同一个分支来的两条树链。这样枚举并且更新信息,还要存方案数。

 

程序:

#include"stdio.h"
#include"string.h"
#include"iostream"
#include"map"
#include"string"
#include"queue"
#include"stdlib.h"
#include"math.h"
#define M 30009
#define eps 1e-10
#define inf 1000000000
#define mod 1000000000
using namespace std;
struct node
{
    int u,w,v,next;
}edge[M*4];
struct Edge
{
    int u,v,w;
}p[M*4];
struct st
{
    int dis;
    __int64 k;
}mp[M];
int t,k,cnt,maxi,MN,ID;
__int64 ans;
int head[M],use[M],dis[M],id[M],num[M],son[M],limit[M];
int cmp(const void *a,const void *b)
{
    if((*(struct node*)a).u==(*(struct node*)b).u)
        return (*(struct node*)b).v-(*(struct node*)a).v;
    else
        return (*(struct node*)b).u-(*(struct node*)a).u;
}
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 spfa(int s,int n)
{
    queue<int>q;
    int i;
    for(i=1;i<=n;i++)
        dis[i]=inf;
    memset(use,0,sizeof(use));
    dis[s]=0;
    use[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        use[u]=0;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(dis[v]>dis[u]+edge[i].w)
            {
                dis[v]=dis[u]+edge[i].w;
                p[v].u=u;
                p[v].w=edge[i].w;
                if(!use[v])
                {
                    use[v]=1;
                    q.push(v);
                }
            }
        }
    }
}
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);
        }
    }
}
void get_root(int root,int u,int f)
{
    dfs_size(u,f);
    MN=inf;
    dfs_root(root,u,f);
}
void dfs_dis(int u,int f,int Rank,int w)
{
    id[cnt++]=u;
    num[u]=Rank;
    dis[u]=w;
    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,Rank+1,w+edge[i].w);
        }
    }
}
void cal(int u,int f)
{
    cnt=0;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(f==v||use[v])continue;
        int last=cnt;
        dfs_dis(v,u,2,edge[i].w);
        for(int j=last;j<cnt;j++)
        {
            if(num[id[j]]==k)//根节点的子链点的个数刚好是k
            {
                if(maxi<dis[id[j]])//更新最长距离,同时更新数量
                {
                    maxi=dis[id[j]];
                    ans=1;
                }
                else if(maxi==dis[id[j]])//相等的话ans++;
                {
                    ans++;
                }
            }
            else if(num[id[j]]<k&&mp[k-num[id[j]]].k)//当当前子链点数小于k,则寻找k-num的子链,
            {
                if(maxi<dis[id[j]]+mp[k-num[id[j]]].dis)//更新最长距离
                {
                    maxi=dis[id[j]]+mp[k-num[id[j]]].dis;
                    ans=mp[k-num[id[j]]].k;
                }
                else if(maxi==dis[id[j]]+mp[k-num[id[j]]].dis)//有相等的话ans加上k-num的链的个数
                {
                    ans+=mp[k-num[id[j]]].k;
                }
            }
        }
        for(int j=last;j<cnt;j++)//转化为对立链(即不包含根节点的链)
        {
            if(mp[num[id[j]]-1].dis<dis[id[j]])
            {
                mp[num[id[j]]-1].dis=dis[id[j]];
                mp[num[id[j]]-1].k=1;
            }
            else if(mp[num[id[j]]-1].dis==dis[id[j]])
            {
                mp[num[id[j]]-1].k++;
            }
        }
    }
    for(int i=0;i<cnt;i++)//注意此时更新mp不要写成memset的形式,否则会超时
        mp[i].k=mp[i].dis=0;
}
void dfs_ans(int u,int f)
{
    get_root(u,u,f);
    cal(ID,ID);
    use[ID]=1;
    for(int i=head[ID];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(!use[v])
        {
             dfs_ans(v,v);
        }
    }
}
void solve()
{
    memset(mp,0,sizeof(mp));
    memset(use,0,sizeof(use));
    ans=maxi=0;
    dfs_ans(1,1);
    printf("%d %I64d\n",maxi,ans);
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n,m,i;
        scanf("%d%d%d",&n,&m,&k);
        for(i=0;i<m*2;i+=2)
        {
            scanf("%d%d%d",&p[i].u,&p[i].v,&p[i].w);
            p[i^1].u=p[i].v;
            p[i^1].v=p[i].u;
            p[i^1].w=p[i].w;
        }
        init();
        qsort(p,m*2,sizeof(p[0]),cmp);
        for(i=0;i<m*2;i++)
            add(p[i].u,p[i].v,p[i].w);
        spfa(1,n);
        init();
        for(i=2;i<=n;i++)
        {
            add(i,p[i].u,p[i].w);
            add(p[i].u,i,p[i].w);
        }
        solve();
    }
}




你可能感兴趣的:(树)