HDU 2586 How far away ? (初学LCA)

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;
}



你可能感兴趣的:(HDU 2586 How far away ? (初学LCA))