POJ1986 Distance Queries(树链剖分LCA)

个人认为是树链剖分求lca的基本题目,当然其他方法也可以做。
大意:给出一棵n个节点的树,k(1<=k<=10000)个询问,每次询问树上两点的距离(每条道路后面输入的那个字母根本没什么卵用)
首先预处理每个节点i到根节点的距离,然后 dis(i,j)=dis(i,root)+dis(j,root)2dis(lca(i,j),root) ,时间复杂度为 O(logn)

#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 40010
using namespace std;
struct tr
{
    int v,w,next;
}edge[MAXN*2];
int head[MAXN],cnt;
void add_edge(int u,int v,int w)
{
    edge[cnt].v = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}
int n,m,q,x,y,z;
int htp[MAXN],son[MAXN],sz[MAXN],dis[MAXN],fa[MAXN],dep[MAXN];
char ops[3];
void dfs1(int u,int f)
{
    int maxnv = 0;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].v;
        if(v == f) continue;
        dep[v] = dep[u]+1;
        dis[v] = dis[u]+edge[i].w;
        fa[v] = u;
        dfs1(v,u);
        sz[u] += sz[v];
        if(sz[v] > sz[maxnv]) maxnv = v;
    }
    sz[u]++;
    son[u] = maxnv;
}
void dfs2(int u,int tp)
{
    htp[u] = tp;
    if(son[u] == 0) return;
    dfs2(son[u],tp);
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].v;
        if(v == fa[u]||v == son[u]) continue;
        dfs2(v,v);
    }
}
int LCA(int u,int v)
{
    while(1)
    {
        if(htp[u] == htp[v])
            return dep[u]<=dep[v]?u:v;
        else if(dep[htp[u]] >= dep[htp[v]])
            u = fa[htp[u]];
        else v = fa[htp[v]];
    }
}
int Dis(int u,int v)
{
    return dis[u]+dis[v]-2*dis[LCA(u,v)];
}
int main()
{
    memset(head,-1,sizeof head);
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= m; i++)
    {
        scanf("%d%d%d%s",&x,&y,&z,ops);
        add_edge(x,y,z);
        add_edge(y,x,z);
    }
    scanf("%d",&q);
    dfs1(1,-1);
    dfs2(1,1);
    for(int i = 1; i <= q; i++)
    {
        scanf("%d%d",&x,&y);
        printf("%d\n",Dis(x,y));
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值