[学习笔记] LCA - 图论

[NOIP2013 提高组] 货车运输

最大生成树+LCA+倍增

好家伙,这道题我写了一个晚上,调了两个晚上,对于这道题我颇有感触。但这道题确实好,实实在在的蓝题,让我发现了许多关于LCA的问题。

首先,这个题给的是一个无向图,并不是个树,为了减少运算量,我们可以把它变成一个树。运用Kruskal算法生成一颗 最大生成树(即这棵树里所有的边权都是最大的)。因为我们要求经过的路径中最短的边的最大值(有点绕),所以这颗最大生成树在保证图原本的连通性的同时,也保证了边权的最大性。

其次,求树上点到点的边权最小值,可以 \(wg[a-b]=min(wg[a-lca], wg[b-lca])\) ,也就是分别找到 \(a\)\(b\)\(lca\) 的最小边权即可。我们知道:求树上路径的长度可以用仅仅一个数组或者是树状数组,但是求一个树链上的最小边权可不是件容易的事。

这让我想到了与树链有关的LCA算法:倍增法。原理就是让 \(a\)\(b\) 不断沿着树链向上跳,最终找到 \(lca\) 。同理,我们也可以让wg数组跟着fa数组一块儿向上跳,最后在统计答案的时候用上即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 1, M = 5e4 + 1;
int n, m, q, f[N], cnt, h[N], fa[21][N], wg[21][N], deep[N], scc[N], lg[N];
bitset<N> flag;
struct Edge{ int u, v, dt; }E[M];
struct edge{ int v, nt, dt; }e[N];
bool cmp(Edge a, Edge b){ return a.dt > b.dt; }
inline void add(int u, int v, int dt){ e[++cnt] = (edge){v, h[u], dt}; h[u] = cnt;}
inline int find(int k){
	if(!f[k]) return k;
	return f[k] = find(f[k]);
}
inline void kruskal(){
	sort(E+1, E+m+1, cmp);
	int eg = 0;
	for(int i=1; i<=m; ++i){
		if(eg == n-1) break;
		int fa = find(E[i].u), fb = find(E[i].v);
		if(fa == fb) continue;
		else{
			++eg;
			f[fa] = fb;
			add(E[i].u, E[i].v, E[i].dt), add(E[i].v, E[i].u, E[i].dt);
		}
	}
}
inline void dfs(int k, int f){
	scc[k] = scc[f];
	flag[k] = 1;
	deep[k] = deep[f] + 1;
	for(int i=h[k]; i; i=e[i].nt){
		int v = e[i].v;
		if(!flag[v]){
			wg[0][v] = e[i].dt;
			fa[0][v] = k;
			dfs(v, k);
//			for(int t=1; t<=lg[deep[v]]; ++t){
//				fa[t][v] = fa[t-1][fa[t-1][v]];
//				wg[t][v] = min(wg[t-1][v], wg[t-1][fa[t-1][v]]);
//			}
		}
	}
}
inline int getans(int a, int b){
	int ans = INT_MAX;
	if(deep[a] < deep[b]) swap(a, b);
//	for(int i=20; i>=0; --i){
//		if(deep[fa[i][a]] >= deep[b]){
//			ans = min(ans, wg[i][a]);
//			a = fa[i][a];
//		}
//	}
	while(deep[a] != deep[b]){
		ans = min(ans, wg[lg[deep[a]-deep[b]]][a]);
		a = fa[lg[deep[a]-deep[b]]][a];
	}
	if(a == b) return ans;
	for(int i=lg[deep[a]]; i>=0; --i)
		if(fa[i][a] != fa[i][b]){
			ans = min({ans, wg[i][a], wg[i][b]});
			a = fa[i][a], b = fa[i][b];
		}
	return min({ans, wg[0][a], wg[0][b]});
}
int main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin>>n>>m;
	lg[0] = -1;
	for(int i=1; i<=n; ++i) lg[i] = lg[i>>1] + 1;
	for(int i=1; i<=m; ++i) cin>>E[i].u>>E[i].v>>E[i].dt;
	kruskal();
	deep[0] = -1;
	for(int i=1; i<=n; ++i){
		if(!flag[i]){
			++scc[0];
			dfs(i, 0);
			fa[0][i] = i;
			wg[0][i] = INT_MAX;
		}
	}
//	for(int j=1; j<=n; ++j){
//		for(int i=1; i<=20; ++i){
//			fa[i][j] = fa[i-1][fa[i-1][j]];
//			wg[i][j] = min(wg[i-1][j], wg[i-1][fa[i-1][j]]);
//		}
//	}
	for(int i=1; i<=20; ++i){
		for(int j=1; j<=n; ++j){
			fa[i][j] = fa[i-1][fa[i-1][j]];
			wg[i][j] = min(wg[i-1][j], wg[i-1][fa[i-1][j]]);
		}
	}
	cin>>q;
	for(int i=1, a, b; i<=q; ++i){
		cin>>a>>b;
		if(scc[a] != scc[b]) cout<<"-1\n";
		else cout<<getans(a, b)<<'\n';
	}
	return 0;
}

没写完。

posted @ 2024-04-11 22:24  XiaoLe_MC  阅读(5)  评论(0编辑  收藏  举报