题意:n个点m条边,每条边有一个权值,有q个询问,每次询问两点间的一条路径,使得这条路径上权值最大的边最小。
思路:很容易想到最小瓶颈路,但是查询太多,会超时,可以预处理出最小生成树,则问题转化为一棵树上的两点间路径中权值最大的那条边,设这两点为u,v,可以得到dist(u,v)=max(dist(u,lca(u,v)),dist(v,lca(v,lca))),其中lca(u,v)表示u和v的最近公共祖先,用倍增的思想预处理出每个结点的2^i的祖先fa[u][i],然后在离线求出lca后顺便计算出答案即可。
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<iostream> #include<algorithm> #include<vector> #include<map> #include<queue> #include<stack> #include<string> #include<map> #include<set> #define eps 1e-6 #define LL long long using namespace std; const int maxn = 55000; const int maxm = 1000000 + 10000; //const int INF = 0x3f3f3f3f; int n, m, dep[maxn]; int pnt[maxn], ans[maxn], fa[maxn][20], dist[maxn][20]; vector<int> G[maxn], W[maxn], query[maxn], num[maxn]; int u[maxm], v[maxm], w[maxm], p[maxn], r[maxm]; bool cmp(const int i, const int j) { return w[i] < w[j]; } int find(int x) { return p[x] == x ? x : p[x] = find(p[x]); } int Kruscal() { int ans = 0; for(int i = 1; i <= n; i++) p[i] = i; //初始化并查集 for(int i = 0; i < m; i++) r[i] = i; //初始化边序号 sort(r, r+m, cmp); for(int i = 0; i < m; i++) { int e = r[i]; int x = find(u[e]), y = find(v[e]); if(x != y) { ans += w[e]; p[x] = y; G[u[e]].push_back(v[e]); G[v[e]].push_back(u[e]); W[u[e]].push_back(w[e]); W[v[e]].push_back(w[e]); } } return ans; } int solve(int u, int lca) { int d = dep[u] - dep[lca]; //if(u==4&&lca==3) cout << dep[u] << " " << dep[lca] << endl; int ans = 0; for(int i = 17; i >= 0; i--) { if((1<<i) <= d) { d -= (1<<i); ans = max(ans, dist[u][i]); //if(u==4&&lca==3) cout << ans << endl; u = fa[u][i]; } } return ans; } bool vis[maxn]; int find_pnt(int x) { if(x == pnt[x]) return x; return pnt[x] = find_pnt(pnt[x]); } void dfs(int u) { vis[u] = 1; pnt[u] = u; int sz1 = G[u].size(); for(int i = 0; i < sz1; i++) { int v = G[u][i]; if(vis[v]) continue; dep[v] = dep[u] + 1; fa[v][0] = u; dist[v][0] = W[u][i]; for(int j = 1; j <=17; j++) { if(fa[v][j-1]) fa[v][j] = fa[ fa[v][j-1] ][j-1], dist[v][j] = max(dist[v][j-1], dist[ fa[v][j-1] ][j-1]); else break; } dfs(v); pnt[v] = u; } int sz2 = query[u].size(); for(int i = 0; i < sz2; i++) { int v = query[u][i]; if(vis[v]) { int lca = find_pnt(v); //cout << lca << endl; ans[num[u][i]] = max(solve(u, lca), solve(v, lca)); } } } void init() { memset(vis, 0, sizeof(vis)); memset(fa, 0, sizeof(fa)); for(int i = 1; i <= n; i++) { G[i].clear(); W[i].clear(); query[i].clear(); num[i].clear(); } } int main() { //freopen("input.txt", "r", stdin); int kase = 0; while(cin >> n >> m) { if(kase++) puts(""); init(); for(int i = 0; i < m; i++) { scanf("%d%d%d", &u[i], &v[i], &w[i]); } Kruscal(); int q; cin >> q; for(int i = 0; i < q; i++) { int x, y; scanf("%d%d", &x, &y); query[x].push_back(y); query[y].push_back(x); num[x].push_back(i); num[y].push_back(i); } dep[1] = 0; dfs(1); //cout << solve(4, 1) << endl; for(int i = 0; i < q; i++) printf("%d\n", ans[i]); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
UVA 11354 Bond(最小生成树+lca+倍增求祖先节点)
原文地址:http://blog.csdn.net/u014664226/article/details/47981317