hdu - 2586 How far away ? LCA 两种算法模板题

题意:给一个无根树,有q个询问,每个询问两个点,问两点的距离。 

求出lca = LCA(u, v) 结果为dir[u] + dir[v] - dir[lca] * 2

链接:hdu 2586

 1、 DFS + ST在线算法模板1

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>

using namespace std;

const int maxn = 50010;
const int maxm = 1000050;
const int inf = 0x7f7f7f7f;
int rmq[2 * maxn];          //  rmq数组,就是欧拉序列对应的深度序列

struct ST
{
    int mm[2 * maxn];
    int dp[2 * maxn][25];     //  最小值对应的下标
    void init(int n) {
        mm[0] = -1;
        for (int i = 1; i <= n; i++) {
            mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1 : mm[i - 1];
            dp[i][0] = i;
        }
        for (int j = 1; j <= mm[n]; j++) {
            for (int i = 1; i + (1 << j) - 1 <= n; i++) {
                dp[i][j] = rmq[dp[i][j - 1]] < rmq[dp[i + (1 << (j - 1))][j - 1]] ? dp[i][j - 1] : dp[i + (1 << (j - 1))][j - 1];
            }
        }
    }
    int query(int a, int b) { //  查询[a,b]之间最小值的下标
        if (a > b) {
            swap(a, b);
        }
        int k = mm[b - a + 1];
        return rmq[dp[a][k]] <= rmq[dp[b - (1 << k) + 1][k]] ? dp[a][k] : dp[b - (1 << k) + 1][k];
    }
};

//  边的结构体定义
struct Edge {
    int to, next;
    int w;
}edge[maxn * 2];

int tot, head[maxn];
int F[maxn * 2];        //  欧拉序列,就是dfs遍历的顺序,长度为2*n-1,下标从1开始
int P[maxn];            //  P[i]表示点i在F中第一次出现的位置
int dir[maxn];
int cnt;
ST st;
bool vis[maxn];

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

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 dfs(int u, int dep)
{
    vis[u] = true;
    F[++cnt] = u;
    rmq[cnt] = dep;
    P[u] = cnt;
    for (int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].to;
        if (vis[v]) {
            continue;
        }
        dir[v] = dir[u] + edge[i].w;
        dfs(v, dep + 1);
        F[++cnt] = u;
        rmq[cnt] = dep;
    }
}

void LCA_init(int root, int node_num)   //  查询LCA前的初始化
{
    cnt = 0;
    dfs(root, 0);
    st.init(2 * node_num - 1);
}

int query_lca(int u, int v)             //  查询u,v的lca编号
{
    return F[st.query(P[u], P[v])];
}

bool flag[maxn];

int main()
{
    int T;
    int n, m;
    int u, v, w;
    scanf("%d", &T);
    while(T--) {
        scanf("%d %d", &n, &m);
        init();
        memset(flag, false, sizeof(flag));
        for (int i = 1; i < n; i++) {
            scanf("%d %d %d", &u, &v, &w);
            addedge(u, v, w);
            addedge(v, u, w);
            flag[v] = true;
        }
        int root;
        for (int i = 1; i <= n; i++) {
            if (!flag[i]) {
                root = i;
                break;
            }
        }
        dir[root] = 0;
        LCA_init(root, n);
        while(m--) {
            scanf("%d %d", &u, &v);
            int lca = query_lca(u, v);
            printf("%d\n", dir[u] + dir[v] - dir[lca] * 2);
        }
    }
    return 0;
}

DFS + ST在线算法模板2

#include <stdio.h>  
#include <string.h>  
#include <algorithm>  
#include <iostream>  
#include <math.h>  
using namespace std;  
const int maxn = 40010;  
const int maxm = 25;  
  
int _pow[maxm], m, n;  
int head[maxn], ip;  
int ver[maxn*2], R[maxn*2], first[maxn], dir[maxn], dp[maxn*2][maxm], tot;  
bool vis[maxn];  
  
void init()  
{  
    memset(vis, false, sizeof(vis));  
    memset(head, -1, sizeof(head));  
    ip = 0;  
}  
  
struct node {  
    int v, w, next;  
}edge[maxn*2];  
  
void addedge(int u, int v, int w)  
{  
    edge[ip].v = v, edge[ip].w = w, edge[ip].next = head[u], head[u] = ip++;  
}  
  
void dfs(int u, int dep)  
{  
    vis[u] = true;  
    ver[++tot] = u, first[u] = tot, R[tot] = dep;  
    for(int i = head[u]; i != -1; i = edge[i].next) {  
        int v = edge[i].v;  
        int w = edge[i].w;  
        if(!vis[v]) {  
            dir[v] = dir[u] + w;  
            dfs(v, dep + 1);  
            ver[++tot] = u, R[tot] = dep;  
        }  
    }  
}  
  
void ST(int len)  
{  
    int k = (int)log((double)len) / (log(2.0));  
    for(int i = 1; i <= len; i++) {  
        dp[i][0] = i;  
    }  
    for(int j = 1; j <= k; j++) {  
        for(int i = 1; i + _pow[j] - 1 <= len; i++) {  
            int a = dp[i][j-1], b = dp[i + _pow[j - 1]][j - 1];  
            if(R[a] < R[b])  
                dp[i][j] = a;  
            else  
                dp[i][j] = b;  
        }  
    }  
}  
  
int RMQ(int x, int y)   
{  
    int k = (int)log((double)(y - x + 1) / log(2.0));  
    int a = dp[x][k], b = dp[y - _pow[k] + 1][k];  
    if(R[a] < R[b])  
        return a;  
    else  
        return b;  
}  
  
int LCA(int u, int v)  
{  
    int x = first[u], y = first[v];  
    if(x > y) swap(x, y);  
    int res = RMQ(x, y);  
    return ver[res];  
}  
  
int main()  
{  
    for(int i = 0; i < maxn; i++) _pow[i] = (1 << i);  
    int T;  
    scanf("%d", &T);  
    while(T--) {  
        scanf("%d %d", &n, &m);  
        init();  
        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);  
        }  
        tot = 0, dir[1] = 0;  
        dfs(1, 1);  
        ST(2 * n - 1);  
        while(m--) {  
            int u, v;  
            scanf("%d %d", &u, &v);  
            int lca = LCA(u, v);  
            printf("%d\n", dir[u] + dir[v] - 2 * dir[lca]);  
        }  
    }  
    return 0;  
}  

2、Tarjan离线算法模板

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>

using namespace std;

const int maxn = 50010;
const int maxm = 1010;    //  查询数的最大值
const int inf = 0x7f7f7f7f;

int F[maxn];                //  需要初始化为-1
int dir[maxn];
bool vis[maxn];             //  访问标记
int ancestor[maxn];         //  祖先

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

struct edge {
    int u;
    int v, next;
    int lca;              //  查询编号
} edge1[maxn * 2];
int head1[maxn], tt;

int Find(int x)
{
    if (F[x] == -1) {
        return x;
    }
    return F[x] = Find(F[x]);
}

void unions(int u, int v)
{
    int t1 = Find(u);
    int t2 = Find(v);
    if (t1 != t2) {
        F[t2] = t1;
    }
}

void init()
{
    tot = 0;
    memset(head, -1, sizeof(head));
    tt = 0;
    memset(head1, -1, sizeof(head1));
    memset(vis, false, sizeof(vis));
    memset(F, -1, sizeof(F));
    memset(ancestor, 0, sizeof(ancestor));
    memset(dir,0,sizeof(dir));
}

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

void add_edge1(int u, int v)
{
    edge1[tt].u = u;
    edge1[tt].v = v;
    edge1[tt].next = head1[u];
    edge1[tt].lca = -1;
    head1[u] = tt++;
    edge1[tt].u = v;
    edge1[tt].v = u;
    edge1[tt].next = head1[v];
    edge1[tt].lca = -1;
    head1[v] = tt++;
}

void tarjan(int u)
{
    ancestor[u] = u;
    vis[u] = true;
    for (int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].to;
        if (vis[v]) {
            continue;
        }
        dir[v] = dir[u] + edge[i].w;
        tarjan(v);
        unions(u, v);
    }
    for (int i = head1[u]; i != -1; i = edge1[i].next) {
        int v = edge1[i].v;
        if (vis[v]) {
            edge1[i].lca = edge1[i ^ 1].lca = ancestor[Find(v)];
        }
    }
}

int main()
{
    int n, m;
    int u, v, k, w;
    int t;
    scanf("%d", &t);
    while (t--) {
        scanf("%d %d", &n, &m);
        init();
        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 < m; i++) {
            scanf("%d %d", &u, &v);
            add_edge1(u, v);
        }
        dir[1] = 0;
        tarjan(1);
        for(int i = 0; i < m; i++) {
            int s = i * 2;
            u = edge1[s].u;
            v = edge1[s].v;
            int lca = edge1[s].lca;
            printf("%d\n", dir[u] + dir[v] - 2 * dir[lca]);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值