Codevs2370 小机房的树

  • 题目大意:给定一棵无根树,树边带权,求树上两点的最近距离。
  • 思路:典型的LCA问题,采用倍增的方法。这里介绍一下倍增法求LCA。记数组fa[i,j]表示节点i向上走 2j 步所能达到的点,在给出直接的父子u,v时记录下fa[u,0]=v,此后利用fa[i][j]=fa[fa[i][j-1]][j-1]就能得到其它的值。利用dfs获得各个节点的深度dep[ ]。节点u,v在寻找LCA时,令dep[u]>dep[v],再让u向上直至dep[u]=dep[v],最后二者一起向上找,直至fa[u,0]=fa[v,0],此时停止寻找,则LCA=fa[u,0]。寻找时,令j从大到小枚举,发现fa[u,j]!=fa[v,j]就往上蹦 2j 步。若要记录花费,只需使用数组g[i,j]表示从节点i向上蹦 2j 步的费用,很容易得到g[i,0]=c以及g[i][j]=g[fa[i][j-1]][j-1]+g[i][j-1]。
  • 注意:这是一个无向图,需要自己选定树根,这里我选的是0,并且需要双向存边,并在DFS中确定树的父子节点关系。
  • 代码如下:
#include
#include
#include
#include
using namespace std;
struct node
{
    int id,len;
    node(int id=0,int len=0):id(id),len(len){}
};
const int maxn=50005;
int n,m,costi[maxn],fa[maxn][25],g[maxn][20],dep[maxn];
int ans;
bool vis[maxn];
vector son[maxn];

void build(int root,int depth)
{
    dep[root]=depth;
    vis[root]=1;
    vector::iterator it;
    for (it=son[root].begin();it!=son[root].end();++it)
    {
        if (!vis[(*it).id])
        {
            fa[(*it).id][0]=root;
            g[(*it).id][0]=(*it).len;
            build((*it).id,depth+1);
        }
    }
}

int adjust(int u,int step)
{
    for (int j=20;j>=0;--j)
    {
        if ((1<1<if (step==0)
              return u;
        }
    }
}

void query(int u,int v)
{
    ans=0;
    if (u==v)
    {
        printf("0");
        return;
    }
    if (dep[u]if (dep[u]!=dep[v])
      u=adjust(u,dep[u]-dep[v]);
    for (int j=20;j>=0;--j)
      if (fa[u][j]!=fa[v][j])
      {
           ans+=g[u][j];
           ans+=g[v][j];
           u=fa[u][j];
           v=fa[v][j];
      }
    if (u!=v)
    {
         ans+=g[u][0];
         ans+=g[v][0];
    }
    printf("%d\n",ans);
}

void init()
{
    scanf("%d",&n);
    int u,v,c;
    memset(fa,0,sizeof(fa));
    memset(vis,0,sizeof(vis));
    for (int i=1;i<=n-1;++i)
    {
        scanf("%d%d%d",&u,&v,&c);
        son[u].push_back(node(v,c));        
        son[v].push_back(node(u,c));
    }
    build(0,0);
    for (int j = 1; j < 20; ++j) 
      for (int i = 1; i <= n; ++i)
      {
          fa[i][j]=fa[fa[i][j-1]][j-1];
          g[i][j]=g[fa[i][j-1]][j-1]+g[i][j-1];
      }
    scanf("%d",&m);
    while (m--)
    {
        scanf("%d%d",&u,&v);
        query(u,v);
    }
}

int main()
{
    init();
    return 0;
}

你可能感兴趣的:(树形结构,树,倍增,LCA)