HDU 2586 How far away ? (初学LCA)

原创 2015年07月07日 20:01:14

How far away ?

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 7739    Accepted Submission(s): 2769


Problem Description
There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house B"? Usually it hard to answer. But luckily int this village the answer is always unique, since the roads are built in the way that there is a unique simple path("simple" means you can't visit a place twice) between every two houses. Yout task is to answer all these curious people.
 

Input
First line is a single integer T(T<=10), indicating the number of test cases.
  For each test case,in the first line there are two numbers n(2<=n<=40000) and m (1<=m<=200),the number of houses and the number of queries. The following n-1 lines each consisting three numbers i,j,k, separated bu a single space, meaning that there is a road connecting house i and house j,with length k(0<k<=40000).The houses are labeled from 1 to n.
  Next m lines each has distinct integers i and j, you areato answer the distance between house i and house j.
 

Output
For each test case,output m lines. Each line represents the answer of the query. Output a bland line after each test case.
 

Sample Input
2 3 2 1 2 10 3 1 15 1 2 2 3 2 2 1 2 100 1 2 2 1
 

Sample Output
10 25 100 100
 


大致题意:求树上任意两点间的距离,显然就是u,v到根的距离减去他们的LCA到根的距离


由于刚学了LCA 用了三种LCA的方法求了


方法一:离线算法tarjan

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 40000+1000;
int head[N];
int fst[N];
struct Edge
{
    int v,w,nxt,id;
}es[N<<1],qury[444];
int cnt,qc;
int ans[222];
int fa[N],ance[N];
void inline add_query(int u,int v,int id)
{
    qury[qc].v=v;
    qury[qc].id=id;
    qury[qc].nxt=fst[u];
    fst[u]=qc++;
    qury[qc].v=u;
    qury[qc].id=id;
    qury[qc].nxt=fst[v];
    fst[v]=qc++;
}
void inline add_edge(int u,int v,int w)
{
    es[cnt].w=w;
    es[cnt].v=v;
    es[cnt].nxt=head[u];
    head[u]=cnt++;
    es[cnt].v=u;
    es[cnt].w=w;
    es[cnt].nxt=head[v];
    head[v]=cnt++;
}
int n,m;
bool vis[N];
int getf(int x)
{
    return x==fa[x]? x: fa[x]=getf(fa[x]);
}
void Merge(int u,int v)
{
    fa[getf(u)]=getf(v);
}
void LCA(int u,int pa)
{
    ance[u]=fa[u]=u;
    for(int i=head[u];~i;i=es[i].nxt)
    {
        int v=es[i].v;
        if(v==pa) continue;
        LCA(v,u);
        Merge(u,v);
        ance[getf(v)]=u;
    }
    vis[u]=1;
    for(int i=fst[u];~i;i=qury[i].nxt)
    {
        int v=qury[i].v;
        if(vis[v]) ans[qury[i].id]=ance[getf(v)];
    }
}
int dp[N];
void makedp(int u,int pa)
{
    for(int i=head[u];~i;i=es[i].nxt)
    {
        int v=es[i].v,w=es[i].w;
        if(v==pa) continue;
        dp[v]=dp[u]+w;
        makedp(v,u);
    }
}
void ini()
{
    memset(head,-1,sizeof(head));
    memset(fst,-1,sizeof(fst));
    cnt=qc=0;
    memset(vis,0,sizeof(vis));
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ini();
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add_edge(u,v,w);
        }
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add_query(u,v,i);
        }
        LCA(1,1);
        makedp(1,1);
        for(int i=0;i<m;i++)
        {
            int j=i<<1;
            printf("%d\n",dp[qury[j].v]+dp[qury[j^1].v]-2*dp[ans[i+1]]);
        }
    }
    return 0;
}

方法二:BFS倍增跳表算法

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;

const int N = 40000+1000;
int head[N];
struct Edge
{
    int v,w,nxt;
}es[N<<1];
int cnt;
inline void add_edge(int u,int v,int w)
{
    es[cnt].v=v;
    es[cnt].w=w;
    es[cnt].nxt=head[u];
    head[u]=cnt++;
    es[cnt].v=u;
    es[cnt].w=w;
    es[cnt].nxt=head[v];
    head[v]=cnt++;
}

int dp[N];

int dep[N];
bool vis[N];
int pa[N][20];
void bfs()
{
    queue<int>q;
    q.push(1);
    pa[1][0]=1;
    vis[1]=1;
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=1;i<20;i++) pa[u][i]=pa[pa[u][i-1]][i-1];
        for(int i=head[u];~i;i=es[i].nxt)
        {
            int v=es[i].v,w=es[i].w;
            if(vis[v]==0)
            {
                vis[v]=1;
                dp[v]=dp[u]+w;
                pa[v][0]=u;
                dep[v]=dep[u]+1;
                q.push(v);
            }
        }
    }
}
int LCA(int u,int v)
{
    if(dep[u]>dep[v]) swap(u,v);
    for(int det=dep[v]-dep[u],i=0;det;i++,det>>=1)
        if(det&1) v=pa[v][i];
    if(v==u) return v;
    for(int i=20-1;i>=0;i--)
        if(pa[u][i]!=pa[v][i]) v=pa[v][i],u=pa[u][i];
    return pa[u][0];
}
int n,m;
void ini()
{
    memset(head,-1,sizeof(head));
    cnt=0;
    memset(vis,0,sizeof(vis));
    dp[1]=0;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        ini();
        for(int i=1;i<n;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add_edge(u,v,w);
        }
        bfs();
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            int lcav = LCA(u,v);
            int ans = dp[u]+dp[v]-2*dp[lcav];
            printf("%d\n",ans);
        }
    }
    return 0;
}


方法三:基于dfs的RMQ(dfs的vs记录的时间戳不是开始进入的时间戳和退出此点的时间戳,而是整个dfs路径的时间戳,每个点都可能经过2次以上,不过由于枝叶只经过一次,可以证明总次数不超过2*MAX_V)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;

const int N = 40000+1000;
int head[N];
struct Edge
{
    int v,w,nxt;
}es[N<<1];
int cnt;
inline void add_edge(int u,int v,int w)
{
    es[cnt].v=v;
    es[cnt].w=w;
    es[cnt].nxt=head[u];
    head[u]=cnt++;
    es[cnt].v=u;
    es[cnt].w=w;
    es[cnt].nxt=head[v];
    head[v]=cnt++;
}

int dp[N];
int index;
int vs[N*2],id[N],dep[N];
int lca[N*2][20];
bool vis[N];
void dfs(int u,int h)
{
    vis[u]=1;
    id[u]=++index;
    vs[index]=u;
    dep[u]=h;
    for(int i=head[u];~i;i=es[i].nxt)
    {
        int v=es[i].v,w=es[i].w;
        if(vis[v]) continue;
        dp[v]=dp[u]+w;
        dfs(v,h+1);
        vs[++index]=u;
    }
}

int mm[N];
void ini()
{
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
    cnt=index=0;
    dp[1]=0;
}
int n,m;
int main()
{
    mm[0]=-1;
    for(int i=1;i<=N-1;i++)mm[i]= (((i-1)&i)==0)? mm[i-1]+1:mm[i-1];
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        ini();
        for(int i=1;i<n;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add_edge(u,v,w);
        }
        dfs(1,0);
        for(int i=1;i<=index;i++) lca[i][0]=vs[i];
        for(int j=1;j<20;j++)
            for(int i=1;i+(1<<j)-1<=index;i++)
            {
                int a=lca[i][j-1],b=lca[i+(1<<(j-1))][j-1];
                lca[i][j] = dep[a]<dep[b] ? a:b;
            }

        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            int l=min(id[u],id[v]);
            int r=max(id[u],id[v]);
            int k=mm[r-l+1];
            int a=lca[l][k],b=lca[r-(1<<k)+1][k];
            int lcav = dep[a]<dep[b]? a:b;
            int ans = dp[u]+dp[v]-2*dp[lcav];
            printf("%d\n",ans);
        }
    }
    return 0;
}



版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kalilili/article/details/46793621

HDU 2586 How far away?(LCA使用详解)

题目:DescriptionThere are n houses in the village and some bidirectional roads connecting them. Every ...
  • nameofcsdn
  • nameofcsdn
  • 2016-08-17 15:56:09
  • 2783

HDU-2586-How far away ?(倍增求LCA模板)

题目链接:HDU-2586-How far away ?倍增求LCA: 1.先转化成有根树。 2.dfs求出每个点的深度 3.初始化anc数组 4.倍增查询#include using nam...
  • jinglinxiao
  • jinglinxiao
  • 2017-03-28 18:33:07
  • 211

hdu2586How far away ?【LCA tarjan求最短距离】

这个题比普通的LCA只多了一个求距离,然后就把我华丽丽的困住了== 当然了,我用的LCA是离线版的并查集那种模板是邝斌的那种,LCA 不难理解,就是dfs树+并查集 ,但是距离加在哪里?? 机智如...
  • zhou_yujia
  • zhou_yujia
  • 2015-11-25 21:39:27
  • 736

HDU 2586 How far away ?

How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T...
  • IAccepted
  • IAccepted
  • 2015-01-24 00:38:35
  • 3083

HDU2586How far away ?(LCA-在线ST+ tarjan离线)

How far away ?Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) To...
  • Viscu
  • Viscu
  • 2017-04-12 12:04:39
  • 244

Hdu 2586 How far away ?

LCA的应用
  • real_Rickys
  • real_Rickys
  • 2017-08-18 15:22:15
  • 76

【HDU】2586 How far away ? LCA求树上点对最近距离

How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...
  • u013368721
  • u013368721
  • 2014-07-02 22:13:37
  • 1131

7_6_A题 How far away?题解[hdu 2586](LCA Tarjan算法)

题目链接题意一个村子中的房子被一些长度不等的道路链接,保证无环(即构成一颗树),给出一些询问,询问两个房子之间的距离是多少。思路很明显的求最近公共祖先,使用Tarjan算法,在求LCA的过程中,记录节...
  • baidu_19306071
  • baidu_19306071
  • 2016-07-06 21:06:15
  • 221

【杭电oj2586】How far away ?

How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...
  • better_space
  • better_space
  • 2016-09-18 20:53:34
  • 422

hdu2586How far away ?

用tarjan解hdu2586,稍微谈了这个算法的实现,欢迎讨论
  • chaoweilanmao
  • chaoweilanmao
  • 2014-07-12 12:13:54
  • 1023
收藏助手
不良信息举报
您举报文章:HDU 2586 How far away ? (初学LCA)
举报原因:
原因补充:

(最多只允许输入30个字)