HDU - 2586 How far away ?LCA 模板题(倍增||Tarjan)

104 篇文章 0 订阅

题意:

给出一个带权无向图,询问两个点之间得距离。

分析:

求出DFS求出每个节点到根节点的距离,然后计算出最近公共祖先设为X,那么可以知道答案为  dis[u]+dis[v]-2*dis[X] 

直接套用LCA就可求出解。

 Tarjan

/*
 * LCA离线算法,Tarjan
 * 复杂度O(n+Q);
 */
#include<bits/stdc++.h>

using namespace std;

const int maxn = 4e4+10;
const int maxq = 210;//查询数的最大值

//并查集部分
int fa[maxn];//需要初始化

int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}

void bing(int u,int v){
    int t1 = find(u);
    int t2 = find(v);
    if(t1 != t2)
        fa[t2] = t1;
}

//************************
bool vis[maxn];//访问标记

struct Edge{
    int to,next,w;
}edge[maxn*2];

int head[maxn],tot;

void addedge(int u,int v,int w){
    edge[tot].to = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

struct Query{
    int u,q,next;
    int index;//查询编号
}query[maxq*2];

int answer[maxq];//存储最后的查询结果,下标0~Q-1
int h[maxn];
int tt;
int Q;

void add_query(int u,int v,int index){
    query[tt].u=u;
    query[tt].q = v;
    query[tt].next = h[u];
    query[tt].index = index;
    h[u] = tt++;
    query[tt].u=v;
    query[tt].q = u;
    query[tt].next = h[v];
    query[tt].index = index;
    h[v] = tt++;
}

void init(){
    tot = 0;
    memset(head,-1,sizeof(head));
    tt = 0;
    memset(h,-1,sizeof(h));
    memset(vis,false,sizeof(vis));
    for(int i=0;i<maxn;i++)fa[i]=i;
}

int dis[maxn];

void LCA(int u){
    vis[u] = true;
    for(int i = head[u];i != -1;i = edge[i].next){
        int v = edge[i].to,w=edge[i].w;
        if(vis[v])continue;
        dis[v]=dis[u]+w;
        LCA(v);
        bing(u,v);
        fa[v]=u;
    }
    for(int i = h[u];i != -1;i = query[i].next){
        int v = query[i].q;
        if(vis[v]){
            answer[query[i].index] = find(v);
        }
    }
}

int main(){
    int n,T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&Q);
        init();
        int u,v,w;
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        for(int i=0;i<Q;i++){
            scanf("%d%d",&u,&v);
            add_query(u,v,i);
        }
        dis[1]=0;
        LCA(1);
        for(int i=0;i<tt;i+=2){
            printf("%d\n",dis[query[i].u]+dis[query[i].q]-2*dis[answer[query[i].index]]);
        }
    }
    return 0;
}

倍增

/*
 *
 * LCA 在线算法
 */
#include<bits/stdc++.h>

using namespace std;

const int maxn = 4e4+10;
const int DEG = 20;

struct Edge{
    int to,next,w;
}edge[maxn*2];

int head[maxn],tot;

void addedge(int u,int v,int w){
    edge[tot].to = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

void init(){
    tot = 0;
    memset(head,-1,sizeof(head));
}

int fa[maxn][DEG];//fa[i][j]表示结点i的第2^j个祖先
int deg[maxn];//深度数组
int dis[maxn];

void bfs(int root){
    queue<int>que;
    deg[root] = 0;
    fa[root][0] = root;
    que.push(root);
    while(!que.empty()){
        int tmp = que.front();
        que.pop();
        for(int i = 1;i < DEG;i++)
            fa[tmp][i] = fa[fa[tmp][i-1]][i-1];
        for(int i = head[tmp]; i != -1;i = edge[i].next){
            int v = edge[i].to,w=edge[i].w;
            if(v == fa[tmp][0])continue;
            dis[v]=dis[tmp]+w;
            deg[v] = deg[tmp] + 1;
            fa[v][0] = tmp;
            que.push(v);
        }

    }
}

int LCA(int u,int v){
    if(deg[u] > deg[v])swap(u,v);
    int hu = deg[u], hv = deg[v];
    int tu = u, tv = v;
    for(int det = hv-hu, i = 0; det ;det>>=1, i++)
        if(det&1)
            tv = fa[tv][i];
    if(tu == tv)return tu;
    for(int i = DEG-1; i >= 0; i--){
        if(fa[tu][i] == fa[tv][i])
            continue;
        tu = fa[tu][i];
        tv = fa[tv][i];
    }
    return fa[tu][0];
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        init();
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        dis[1]=0;
        bfs(1);
        while(m--){
            int u,v;
            scanf("%d%d",&u,&v);
            printf("%d\n",dis[u]+dis[v]-2*dis[LCA(u,v)]);
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值