HDU 2586 (LCA模板题)

题目链接http://acm.hdu.edu.cn/showproblem.php?pid=2586

题目大意:在一个无向树上,求一条链权和。

解题思路

       0

       |

       1

     /  

   2      3

设dist[i]为i到根0的链和,求法(Dfs过程中dist[v]=dist[u]+e[i].w)

对于树中任意两点形成的链,可以通过LCA最近公共祖先剖分。

比如2->3,就可以经过LCA点1:  2->1->3

链和=dist[u]+dist[v]-2*dist[LCA[u,v]]

(0-1-2)+(0-1-3)-2*(0-1)=(2-1-3),有点容斥原理的味道。

LCA比较快的是Tarjan离线法,把全部query也做成一个无向树,离线处理。

过程分为两个stage,stage 1对连接树处理,stage 2对query树处理。

两个stage可以颠倒。正写法 倒写法。在Tarjan(u)中,并查集find(v),可以获得LCA。

LCA存储比较头疼,由于LCA是双向共享的。可以建个ancestor数组,索引是查询序号。

也可以直接存在query树的链式前向星中。

本题双向建一个无向树,任意选择一个起点作为root做LCA都可以。

#include "cstdio"
#include "cstring"
#define maxn 40005
#define maxm 205
int head[maxn],qhead[maxn],dist[maxn],tot1,tot2,f[maxn],vis[maxn],ancestor[maxn];
struct Edge
{
    int to,next,w;
}e[maxn*2];
struct Query
{
    int from,to,next,idx;
}q[maxn*2];
void addedge(int u,int v,int w)
{
    e[tot1].to=v;
    e[tot1].w=w;
    e[tot1].next=head[u];
    head[u]=tot1++;
}
void addquery(int u,int v,int idx)
{
    q[tot2].from=u;
    q[tot2].to=v;
    q[tot2].next=qhead[u];
    q[tot2].idx=idx;
    qhead[u]=tot2++;
}
int find(int x) {return x!=f[x]?f[x]=find(f[x]):x;}
void Union(int u,int v)
{
    u=find(u),v=find(v);
    if(u!=v) f[v]=u;
}
void LCA(int u)
{
    vis[u]=true;
    f[u]=u;
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        int v=e[i].to,w=e[i].w;
        if(!vis[v])
        {
            dist[v]=dist[u]+w;
            LCA(v);
            Union(u,v);
        }
    }
    for(int i=qhead[u];i!=-1;i=q[i].next)
    {
        int v=q[i].to;
        if(vis[v]) ancestor[q[i].idx]=find(v);
        //or storage e[i].lca=e[i^1].lca=find(v)
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    int T,n,m,u,v,c;
    scanf("%d",&T);
    while(T--)
    {
        tot1=tot2=0;
        memset(head,-1,sizeof(head));
        memset(qhead,-1,sizeof(qhead));
        memset(vis,0,sizeof(vis));
        dist[1]=0;
        scanf("%d%d",&n,&m);
        for(int i=0;i<n-1;i++)
        {
            scanf("%d%d%d",&u,&v,&c);
            addedge(u,v,c);
            addedge(v,u,c);
        }
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&u,&v);
            addquery(u,v,i);
            addquery(v,u,i);
        }
        LCA(1);
        for(int i=0;i<tot2;i=i+2)
        {
            int u=q[i].from,v=q[i].to,idx=q[i].idx;
            printf("%d
",dist[u]+dist[v]-2*dist[ancestor[idx]]);
        }
    }
}
原文地址:https://www.cnblogs.com/neopenx/p/4502053.html