HDU 2586 How far away ? (LCA,Tarjan, spfa)

题意:给定N个节点一棵树,现在要求询问任意两点之间的简单路径的距离,其实也就是最短路径距离。

析:用LCA问题的Tarjan算法,利用并查集的优越性,产生把所有的点都储存下来,然后把所有的询问也储存下来,然后从树根开始搜索这棵树,

在搜索子树的时候,把并查集的父结点不断更新,在搜索时计算答案,d[i] 表示从根结点到 i 结点的距离,然后分别计算到两个结点的距离,

再减去2倍的根结点到他们公共最近的结点距离,就OK了。不过这个题有个坑人的地方,不用输出那个空行,否则就是PE。

因为这个题询问比较少,所以我们可以用spfa来做。

代码如下:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <string>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <cstring>
#include <set>
#include <queue>
#include <algorithm>
#include <vector>
#include <map>
#include <cctype>
#include <cmath>
#include <stack>
#define freopenr freopen("in.txt", "r", stdin)
#define freopenw freopen("out.txt", "w", stdout)
using namespace std;

typedef long long LL;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const double inf = 0x3f3f3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-8;
const int maxn = 4e4 + 5;
const int mod = 1e9 + 7;
const int dr[] = {-1, 0, 1, 0};
const int dc[] = {0, 1, 0, -1};
const char *de[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"};
int n, m;
const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
inline int Min(int a, int b){ return a < b ? a : b; }
inline int Max(int a, int b){ return a > b ? a : b; }
inline LL Min(LL a, LL b){ return a < b ? a : b; }
inline LL Max(LL a, LL b){ return a > b ? a : b; }
inline bool is_in(int r, int c){
    return r >= 0 && r < n && c >= 0 && c < m;
}
struct Edge{
    int to, w, next;
};
struct node{
    int to, id, next;
};
Edge a[maxn<<1];
node b[405];
int p[maxn], head[maxn<<1], cnt, cnt1, head1[maxn];
int rec[205][3], d[maxn];
bool vis[maxn];

int Find(int x){  return x == p[x] ? x : p[x] = Find(p[x]); }

void add(int u, int v, int w){
    a[cnt].to = v;
    a[cnt].w = w;
    a[cnt].next = head[u];
    head[u] = cnt++;
}

void add1(int u, int v, int id){
    b[cnt1].to = v;
    b[cnt1].id = id;
    b[cnt1].next = head1[u];
    head1[u] = cnt1++;
}

void Tarjan(int u){
    vis[u] = true;

    for(int i = head1[u]; ~i; i = b[i].next){
        int v = b[i].to;
        if(vis[v])  rec[b[i].id][2] = Find(v);
    }

    for(int i = head[u]; ~i; i = a[i].next){
        int v = a[i].to;
        if(!vis[v]){
            d[v] = d[u] + a[i].w;
            Tarjan(v);
            p[v] = u;
        }
    }
}

int main(){
    int T;  cin >> T;
    while(T--){
        scanf("%d %d", &n, &m);
        for(int i = 0; i <= n; ++i)  p[i] = i;
        int u, v, w;
        cnt = cnt1 = 0;
        memset(head, -1, sizeof head);
        memset(head1, -1, sizeof head1);
        for(int i = 1; i < n; ++i){
            scanf("%d %d %d", &u, &v, &w);
            add(u, v, w);
            add(v, u, w);
        }
        for(int i = 0; i < m; ++i){
            scanf("%d %d", &u, &v);
            add1(u, v, i);
            add1(v, u, i);
            rec[i][0] = u;
            rec[i][1] = v;
        }
        memset(vis, false, sizeof vis);
        d[1] = 0;
        Tarjan(1);

        for(int i = 0; i < m; ++i)
            printf("%d\n", d[rec[i][0]] + d[rec[i][1]] - 2*d[rec[i][2]]);
        //printf("\n");
    }
    return 0;
}

第二种写法:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <string>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <cstring>
#include <set>
#include <queue>
#include <algorithm>
#include <vector>
#include <map>
#include <cctype>
#include <cmath>
#include <stack>
#define freopenr freopen("in.txt", "r", stdin)
#define freopenw freopen("out.txt", "w", stdout)
using namespace std;

typedef long long LL;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const double inf = 0x3f3f3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-8;
const int maxn = 4e4 + 5;
const int mod = 1e9 + 7;
const int dr[] = {-1, 0, 1, 0};
const int dc[] = {0, 1, 0, -1};
const char *de[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"};
int n, m;
const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
inline int Min(int a, int b){ return a < b ? a : b; }
inline int Max(int a, int b){ return a > b ? a : b; }
inline LL Min(LL a, LL b){ return a < b ? a : b; }
inline LL Max(LL a, LL b){ return a > b ? a : b; }
inline bool is_in(int r, int c){
    return r >= 0 && r < n && c >= 0 && c < m;
}
vector<int> G[maxn], w[maxn], q[maxn], id[maxn];
int d[maxn], p[maxn], ans[205];
bool vis[maxn];
int Find(int x){  return x == p[x] ? x : p[x] = Find(p[x]); }

void Tarjan(int u){
    vis[u] = true;
    for(int i = 0; i < G[u].size(); ++i){
        int v = G[u][i];
        if(!vis[v]){
            d[v] = d[u] + w[u][i];
            Tarjan(v);
            p[v] = u;
        }
    }

    for(int i = 0; i < q[u].size(); ++i){
        int v = q[u][i];
        if(vis[v])  ans[id[u][i]] = d[u] + d[v] - 2*d[Find(v)];
    }
}

int main(){
    int T;;  cin >> T;
    while(T--){
        scanf("%d %d", &n, &m);
        for(int i = 1; i <= n; ++i) G[i].clear(), w[i].clear(), q[i].clear(), id[i].clear(), p[i] = i;

        int u, v, val;
        for(int i = 1; i < n; ++i){
            scanf("%d %d %d", &u, &v, &val);
            G[u].push_back(v);
            G[v].push_back(u);
            w[u].push_back(val);
            w[v].push_back(val);
        }
        for(int i = 0; i < m; ++i){
            scanf("%d %d", &u, &v);
            q[u].push_back(v);
            q[v].push_back(u);
            id[u].push_back(i);
            id[v].push_back(i);
        }
        memset(vis, false, sizeof vis);
        d[1] = 0;
        Tarjan(1);

        for(int i = 0; i < m; ++i)
            printf("%d\n", ans[i]);
    }
    return 0;
}

spfa:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <string>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <cstring>
#include <set>
#include <queue>
#include <algorithm>
#include <vector>
#include <map>
#include <cctype>
#include <cmath>
#include <stack>
#define freopenr freopen("in.txt", "r", stdin)
#define freopenw freopen("out.txt", "w", stdout)
using namespace std;

typedef long long LL;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const double inf = 0x3f3f3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-8;
const int maxn = 4e4 + 5;
const int mod = 1e9 + 7;
const int dr[] = {-1, 0, 1, 0};
const int dc[] = {0, 1, 0, -1};
const char *de[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"};
int n, m;
const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
inline int Min(int a, int b){ return a < b ? a : b; }
inline int Max(int a, int b){ return a > b ? a : b; }
inline LL Min(LL a, LL b){ return a < b ? a : b; }
inline LL Max(LL a, LL b){ return a > b ? a : b; }
inline bool is_in(int r, int c){
    return r >= 0 && r < n && c >= 0 && c < m;
}
struct node{
    int to, w, next;
};
node a[maxn<<1];
int cnt;
int head[maxn];
bool vis[maxn];
int d[maxn];
int q[maxn];

void add(int u, int v, int w){
    a[cnt].to = v;
    a[cnt].w = w;
    a[cnt].next = head[u];
    head[u] = cnt++;
}

int spfa(int s, int t){
    memset(vis, false, sizeof vis);
    fill(d, d+n+1, INF);
    int front = 0, rear = 0;
    q[rear++] = s;
    d[s] = 0;

    while(front < rear){
        int u = q[front++];
        vis[u] = false;

        for(int i = head[u]; ~i; i = a[i].next){
            int v = a[i].to;
            if(d[v] > d[u] + a[i].w){
                d[v] = d[u] + a[i].w;
                if(!vis[v]) q[rear++] = v, vis[v] = true;
            }
        }
    }
    return d[t];
}

int main(){
    int T;  cin >> T;
    while(T--){
        scanf("%d %d", &n, &m);
        int u, v, w;
        cnt = 0;
        memset(head, -1, sizeof head);
        for(int i = 1; i < n; ++i){
            scanf("%d %d %d", &u, &v, &w);
            add(u, v, w);
            add(v, u, w);
        }
        while(m--){
            scanf("%d %d", &u, &v);
            printf("%d\n", spfa(u, v));
        }
    }
    return 0;
}
时间: 2025-01-02 16:10:02

HDU 2586 How far away ? (LCA,Tarjan, spfa)的相关文章

HDU 2586 How far away ? (LCA最近公共祖先)

题目地址:HDU 2586 LCA第一发. 纯模板题. 偷懒用的vector,结果一直爆栈.把G++改成C++就过了.. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.h> #include <map> #include <se

hdu 2874 Connections between cities hdu 2586 How far away ? LCA

两道lca模板题,用的是倍增法,nlogn预处理,logn查询. #include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; #define maxn 10100 struct Edge { int u,v,w,next; }e[100100]; int n,m,c; int head[maxn],cnt; int fa[ma

HDU 2586 How far away ? &lt;&lt; LCA转RMQ+ST表 求树上任两点最短距离裸题

此题还有LCA+tarjin离线查询做法,详见这里 关于ST表 解决RMQ问题,dp[i][j]表示从第i位开始长度为(1<<j)的区间的最值 维护的时候采用倍增思想,维护dp[i][j+1]=opt(dp[i][j],dp[i+(1<<j)][j]) 查询的时候,两端点为l,r,则长度len=r-l,寻找一个k,使(1<<k)大于等于len/2,这样就使得 [l,l+(1<<k)]和[r-(1<<k),r]覆盖整个区间 然后此时,就可以直接用s

HDU - 2586 How far away ?(离线Tarjan算法)

1.给定一棵树,每条边都有一定的权值,q次询问,每次询问某两点间的距离. 2.这样就可以用LCA来解,首先找到u, v 两点的lca,然后计算一下距离值就可以了. 这里的计算方法是,记下根结点到任意一点的距离dis[],这样ans = dis[u] + dis[v] - 2 * dis[lca(u, v)] 3. /* 离线算法,LCATarjan 复杂度O(n+Q); */ #include<iostream> #include<stdio.h> #include<stri

HDU 2586 How far away ?(LCA裸题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 #include<bits/stdc++.h> #define lson rt << 1, l, m #define rson rt << 1 | 1, m + 1, r using namespace std; typedef long long ll; static const ll inf = (1 << 32); static const int

HDU - 2586 How far away ?(LCA)

题目大意:给出一张连通图,问两个点之间的距离 解题思路:LCA裸题 #include <cstdio> #include <cstring> #define N 40010 #define M 80010 struct Edge{ int to, next, dis; }E[M]; struct Question { int x, y; }Q[N]; int n, m ,tot; int head[N], dist[N], f[N], LCA[N]; bool vis[N]; vo

【HDU 2586 How far away?】LCA问题 Tarjan算法

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 题意:给出一棵n个节点的无根树,每条边有各自的权值.给出m个查询,对于每条查询返回节点u到v的最短路径的权值和,按查询顺序输出结果. 数据范围:n [2, 40000], m[1, 200] 思路:Tarjan算法:dfs遍历每个点,每遍历完 r 的一个孩子 c, 把 c 并入以 r 为祖先的集合,并处理 c 的所有查询 q:若qi的目标节点 v 已被遍历到,那么一定有lca(c, v) =

hdu 2586 How far away ?倍增LCA

hdu 2586 How far away ?倍增LCA 题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2586 思路: 针对询问次数多的时候,采取倍增求取LCA,同时跟新距离数组 因为 \(2^{16} > 40000\) 所以所以表示祖先的数组dp[][]第二维取到16即可 就这道题来说,与比较tarjan比较,稍快一点 代码: #include <iostream> #include <algorithm> #includ

HDU - 2586 - How far away ?

先上题目: How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4936    Accepted Submission(s): 1866 Problem Description There are n houses in the village and some bidirectional roads conne