HDU-2874Connections between cities(lca模板题,注意是森林)

题目大意

题目链接
After World War X, a lot of cities have been seriously damaged, and we need to rebuild those cities. However, some materials needed can only be produced in certain places. So we need to transport these materials from city to city. For most of roads had been totally destroyed during the war, there might be no path between two cities, no circle exists as well.
Now, your task comes. After giving you the condition of the roads, we want to know if there exists a path between any two cities. If the answer is yes, output the shortest path between them.

Input

Input consists of multiple problem instances.For each instance, first line contains three integers n, m and c, 2<=n<=10000, 0<=m<10000, 1<=c<=1000000. n represents the number of cities numbered from 1 to n. Following m lines, each line has three integers i, j and k, represent a road between city i and city j, with length k. Last c lines, two integers i, j each line, indicates a query of city i and city j.

Output

For each problem instance, one line for each query. If no path between two cities, output “Not connected”, otherwise output the length of the shortest path between them.

Sample Input
1
2
3
4
5
6
5 3 2
1 3 2
2 4 3
5 2 3
1 4
4 5
Sample Output
1
2
Not connected
6

分析

在森林中求lca。
用并查集判断两个点是不是在同一棵树中。
用tarjan离线一直mle。。。无语了, 最后用的倍增才过的。
第一次遇到用时间换空间的题。。。

倍增ac代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

typedef unsigned long long ull;
typedef pair<int, int> P;
#define ms(a, b) memset((a), (b), sizeof(a))
const int N = (int)1e5 + 5;
const int INF = 0x3f3f3f3f;

inline int read() {
    int res = 0;bool f = 0;char ch = getchar();
    while (ch < '0' || ch > '9')  { if (ch == '-') f = 1; ch = getchar(); }
    while (ch <= '9' && ch >= '0') { res = (res << 3) + (res << 1) + ch - '0'; ch = getchar(); }
    return f ? (~ res + 1) : res;
}

struct edge{
	int u, v, w, ne;
}ed[N];

int head[N], dis[N], cnt, n, m;

int deep[N], fa[N][20];

inline void init(){
	ms(head, -1);
	cnt = 0;
	ms(deep, 0);
	ms(dis, 0);
}

inline void add(int u, int v, int w){
	ed[cnt] = {u, v, w, head[u]};
	head[u] = cnt++;
}

inline void dfs(int u, int pre){

	deep[u] = deep[pre] + 1; // deep数组表示深度
	fa[u][0] = pre;          // fa[i][j] 表示i的第2^j辈的祖先
	for (int i = 1; i < 20; ++i)
		fa[u][i] = fa[fa[u][i - 1]][i - 1];  
		// fa[i][j] = fa[fa[i][j - 1]][j - 1]  i的第2^j辈的祖先 等于 i的第2^(j-1)辈的祖先的第2^(j-1)辈的祖先
	for (int i = head[u]; ~i; i = ed[i].ne){
		int v = ed[i].v;
		if(v == pre) continue;
		dis[v] = dis[u] + ed[i].w;
		dfs(v, u);
	}
}

inline int lca(int a, int b){
	if (deep[a] < deep[b]) swap(a, b);
	// 运用二进制的思想 使得deep[a] == deep[b] 比如 7 可以用4 2 1表示
	for (int i = 19; i >= 0; --i)
		if (deep[a] - (1 << i) >= deep[b])
			a = fa[a][i];
	
	if (a == b) return a;
	// 倍增 2^k 步的爬, 找最小的满足 a != b 的
	for (int i = 19; i >= 0; --i){
		if (fa[a][i] != fa[b][i])
			a = fa[a][i], b = fa[b][i];
	}
	return fa[a][0];
}
int t, u, v, w;
int main(){
	t = read();
	while(t--){
		n = read(), m = read();
		init();
		for (int i = 1; i < n; ++i){
			u = read(), v = read(), w = read();
			add(u, v, w);
			add(v, u, w);
		}
		dfs(1, 0);
		while(m--){
			u = read(), v = read();
			int x = lca(u, v);
			printf("%d\n", dis[u] + dis[v] - 2 * dis[x]);
		}
	}
	return 0;
}

tarjan MLE代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <utility>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;

typedef unsigned long long ull;
typedef pair<int, int> P;
#define ms(a, b) memset((a), (b), sizeof(a))
const int N = (int)1e4 + 5;
const int M = (int)1e6 + 10;
const int INF = 0x3f3f3f3f;

inline int read() {
    int res = 0;bool f = 0;char ch = getchar();
    while (ch < '0' || ch > '9')  { if (ch == '-') f = 1; ch = getchar(); }
    while (ch <= '9' && ch >= '0') { res = (res << 3) + (res << 1) + ch - '0'; ch = getchar(); }
    return f ? (~ res + 1) : res;
}

struct edge {
    int u, v, w, ne;
}ed[N << 1], eq[M << 1]; // ed存边 eq离线存询问

int head[N], f[N], hq[N];
int dis[N], ans[N];
int vis[N];
int cnt, n, m, cnt1;

void init(){
    cnt = cnt1 = 0;
    for (int i = 0; i <= n; ++i){
        f[i] = i;
    }
    ms(head, -1);
    ms(hq, -1);
    ms(vis, 0);
    ms(ans, 0);
    ms(dis, -1);
}
inline void add(int &u, int &v, int &w){
    ed[cnt] = {u, v, w, head[u]};
    head[u] = cnt++;
}
inline void addq(int &u, int &v, int &id){
	eq[cnt1] = {u, v, id, hq[u]}; // 在结构体里虽然还是w, 但是咱们用它表示id
	hq[u] = cnt1++;
}


inline int tofind(int &x){
    return (f[x] == x) ? f[x] : (f[x] = tofind(f[x]));
}
inline void tojoin(int &a, int &b){
	a = tofind(a);
	b = tofind(b);
	if (a != b)
		f[b] = a;
}

void tarjan(int &u, int sgn){
	// 分为两部分, 第一部分 建树 第二部分 求ans
    // 第一部分, 建树
    vis[u] = sgn;
    for (int i = head[u]; ~i; i = ed[i].ne){
        int &v = ed[i].v, &w = ed[i].w;
        if (vis[v] == sgn) continue;
        dis[v] = dis[u] + w; // v到根结点的距离
        tarjan(v, sgn);
        f[v] = u; // 这里直接连上(f[v] = u)就行, 
    }

    for (int i = hq[u]; ~i; i = eq[i].ne){ // 与u相关的边
    	int &v = eq[i].v, &id = eq[i].w;
    	if (vis[v] != sgn + 1) continue; // 2 是以为这离线过了的点
    	
    	int lca = tofind(v);
        if (dis[v] != -1)
    	   ans[id] = dis[u] + dis[v] - 2 * dis[lca];
    	
    }
    vis[u] = sgn + 1;
}

int t, u, v, w;
int main(){
	int q;
	while(~scanf("%d%d%d", &n, &m, &q)){
		init(); // init 要放在n之后, 因为这个又被卡了好长时间。。。。。。
	    for (int i = 1; i <= m; ++i){
	    	u = read(), v = read(), w = read();
	        add(u, v, w);
	        add(v, u, w);
	      
	    }
	    
	    for (int i = 0; i < q; ++i){
	    	u = read(), v = read();
	        addq(u, v, i);
	        addq(v, u, i);
	    }
	    for (int i = 1; i <= n; ++i){
	    	if (!vis[i])
	    		tarjan(i, 2 * i);
	    }
	    for (int i = 0; i < q; ++i){
	    	if (!ans[i]) puts("Not connected");
	        else printf("%d\n", ans[i]);
	    }
	}
    return 0;
}
1
恰似你一低头的温柔,娇弱水莲花不胜寒风的娇羞, 我的心为你悸动不休。  --mingfuyan
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值