HDU-2586 (LCA Tarjan & 倍增)

題目連接

題意:

        求一棵樹上任意兩點的最短距離,n個點,m次詢問

數據範圍:  

        2 < n < 40000,    2 < m < 200

思路:(Tarjan)

        LCA板子題,需要一個dis [ ] 數組, dis[ x ] 記錄一下該點 ( x ) 到父親節點 ( find(x) ) 的距離 ,當找到一個匹配點 ( to ) 時,兩個點的最短距離 = (dis[ x ] + dis[ to ] - dis[ find(x) ] * 2);

補習:點擊補習LCA

TarjanAC:

#include<iostream>
#include<cstring>
#include<math.h>
#include<algorithm>
using namespace std;
const int MAXN = 4e4 + 10;
int n, m;
int head[MAXN], head_ask[MAXN], cnt, cnt_ask;
int ans[MAXN], fa[MAXN];
bool vis[MAXN];
int dis[MAXN];	//保存該節點到父親的距離 
struct Edge {
	int to, next, dis;
	int num;	
}edge[MAXN << 1], ask[MAXN << 1];
void add_edge(int u, int v, int dis){
	edge[++cnt].to = v;
	edge[cnt].next = head[u];
	edge[cnt].dis = dis;
	head[u] = cnt;
}
void add_ask(int u, int v, int num) {
	ask[++cnt_ask].to = v;
	ask[cnt_ask].num = num;
	ask[cnt_ask].next = head_ask[u];
	head_ask[u] = cnt_ask;
}
int find(int x) {
	return fa[x] == x ? x : fa[x] = find(fa[x]);
} 
void init() {
	cnt = 1;
	cnt_ask = 1; 
	memset(vis, 0, sizeof(vis));
	memset(dis, 0, sizeof(dis));
	memset(head,0, sizeof(head));
	memset(head_ask,0, sizeof(head_ask));
	fa[n] = n; 
}
void Tarjan(int x) {
	vis[x] = true;
	for(int i = head[x]; i; i = edge[i].next) {
		int to = edge[i].to; 
		if( !vis[to] ) {
			dis[to] = dis[x] + edge[i].dis; 
			Tarjan(to);
			fa[to] = find(x);
		}
	}
	for(int i = head_ask[x]; i; i = ask[i].next) {
		int to = ask[i].to;
		if (vis[to]) {
			ans[ask[i].num] = dis[to] + dis[x] - dis[find(to)] * 2; 
		}
	}
}
int main () {
	int T;
	cin >> T;
	while(T--) {
		cin >> n >> m;
		int x, y, z;
		init();
		for(int i = 1; i < n; ++i) {
			fa[i] = i;
			cin >> x >> y >> z;
			add_edge(x, y, z);
			add_edge(y, x, z);
		}
		for(int i = 1; i <= m; ++i) {
			cin >> x >> y;
			add_ask(x, y, i);
			add_ask(y, x, i);
		}
		Tarjan(1);
		for(int i = 1; i <= m; ++i) {
			cout << ans[i] << endl;
		}
		
	}
	return 0;
} 

倍增:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 40000 + 10;
int head[MAXN], cnt;
int fa[MAXN][20], depth[MAXN], dis[MAXN];	//記錄到根節點的距離 
int n, m;

struct Edge{
	int to, dis, next;
}edge[MAXN << 1];
void add_edge(int u, int v, int dis) {
	edge[++cnt].to = v;
	edge[cnt].dis = dis;
	edge[cnt].next = head[u];
	head[u] = cnt;
}
void init () {
	cnt = 1;
	memset(head, 0, sizeof(head));
	memset(fa, 0, sizeof(fa));
	memset(depth, 0, sizeof(depth));
	memset(dis, 0, sizeof(dis));
}
void DFS(int now, int pre) {
	depth[now] = depth[pre] + 1; 
	fa[now][0] = pre;
	for (int i = 1; (1 << i) <= depth[now]; ++i) {
		fa[now][i] = fa[fa[now][i-1]][i-1];
	}
	for(int i = head[now]; i; i = edge[i].next) {
		int to = edge[i].to;
		if(to != pre){
			dis[to] = dis[now] + edge[i].dis;
			DFS(edge[i].to, now);
		}
			
	}
}
int LCA(int a, int b) {
	if(depth[a] < depth[b]) swap(a, b);
	int dep = depth[a] - depth[b];
	for(int i = 0; (1 << i) <= dep; ++i) {
		if ((1 << i) & dep) 
			a = fa[a][i];
	}
	if(a == b) return a;
	for (int i = log2(depth[a]); i >= 0; --i) {
			//如果父親不同就向上跳, 如果父親相同就減小距離再比較,直到不相同在跳。 
		if (fa[a][i] != fa[b][i]) {
			a = fa[a][i];
			b = fa[b][i];	
		}
	}
	return fa[a][0];
}
int main () {
	int T;
	cin >> T;
	while(T--) {
		init(); 
		cin >> n >> m;
		int x, y, z;
		for(int i = 1; i <= n - 1; ++i) {
			cin >> x >> y >> z;
			add_edge(x, y, z);
			add_edge(y, x, z);	
		}
		DFS(1, 0);
		for(int i = 1; i <= m; ++i) {
			cin >> x >> y;
			int temp = LCA(x, y);
			int ans = dis[x] + dis[y] - 2 * dis[temp];
			cout << ans << endl;
		}
	}	
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章