洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
2016-11-10 23:03
399 查看
传送门
其实NOIP某些年的第三题也并不是很难嘛。。。题目分析:
题目中要求求出某两点之间可以运输的最大重量,也就是这两个点的某条路径上边权最小的边的权值的最大值很显然,题目中的运输最大重量与选择的边数,走法,无关边的权值均无关,可以求出这张图的最大生成树,在树上进行询问,与图中询问结果相同
由于该图不保证连通性,所以实际形成的是一个最大生成森林,那么对于每个询问,直接用并查集判断是否存在,对于存在的询问,考虑优化查询的时间
既然已经把图压缩成了森林,那么就可以使用一些树特有的性质了,,对于树上两个点,求他们之间的边的最小值,,,可以考虑使用倍增了,维护jump数组的基础上,维护low数组,使用low[x][0]表示以x的父亲边的权值,low[x][i]表示x到x的2i代父亲的权值最小值,那么题目中的查询就可以转化为两个点到他们LCA的路径上的最小值,每次倍增寻找结果输出即可
题解:
对原图进行kruskal求最大生成树,在其过程中用邻接表把选择的边建好dfs求出倍增中的dis数组,jump[x][0],low[x][0]
枚举i,j更新jump数组和low数组,jump[x][j]=jump[jump[x][j−1]][j−1] low[x][i]=min(low[x][i−1],low[jump[x][i−1]][i−1])
常规倍增求LCA,设ans初始值为INF,每次与当前的low值取min,输出ans即为结果
Warning:
倍增LCA的j为外层循环!!!倍增LCA的j为外层循环!!!
倍增LCA的j为外层循环!!!
倍增LCA的j为外层循环!!!
倍增LCA的j为外层循环!!!
代码:
#include <cstdio> #include <cstring> #include <algorithm> //#define debug const int inf = 0x3f3f3f3f; const int maxn = 20000; const int maxm = 100000 + 500; struct edge{ int from; int to; int val; }; edge p[maxm]; int n, m; int father[maxn]; int last[maxn], pre[maxm], other[maxm], len[maxm]; int jump[maxn][25]; int dis[maxn]; int low[maxn][25]; int tot = 0; int x, y; void add(int x, int y, int z) { tot++; pre[tot] = last[x]; last[x] = tot; other[tot] = y; len[tot] = z; } int getfather(int x) { if (father[x] == x) return (x); return (father[x] = getfather(father[x])); } bool cmp(edge aa, edge bb) { return (aa.val > bb.val); } void dfs(int x, int come, int comep) { jump[x][0] = come; low[x][0] = comep; dis[x] = dis[come] + 1; for (int p = last[x]; p; p = pre[p]) { int q = other[p]; if (q == come) continue; dfs(q, x, len[p]); } } int main () { //freopen("truck.in", "r", stdin); //freopen("truck.out", "w", stdout); scanf("%d %d", &n, &m); memset(low, 127, sizeof(low)); for (int i = 1; i <= m; i++) { scanf("%d %d %d", &p[i].from, &p[i].to, &p[i].val); } for (int i = 1; i <= n; i++) father[i] = i; std :: sort(p + 1, p + m + 1, cmp); for (int i = 1; i <= m; i++) { int tx = getfather(p[i].from); int ty = getfather(p[i].to); if (tx == ty) continue; father[tx] = ty; add(p[i].from, p[i].to, p[i].val); add(p[i].to, p[i].from, p[i].val); } for (int i = 1; i <= n; i++) { if (dis[i] == 0) { dfs(i, 0, inf); } } for (int j = 1; j <= 20; j++) { for (int i = 1; i <= n; i++) { jump[i][j] = jump[jump[i][j-1]][j-1]; low[i][j] = std :: min(low[i][j-1], low[jump[i][j-1]][j-1]); } } #ifdef debug for (int i = 1; i <= n; i++) { printf("low[%d] = %d\n", i, low[i][0]); } //exit(0); #endif int q; scanf("%d", &q); while (q--) { scanf("%d %d", &x, &y); int tx = getfather(x); int ty = getfather(y); if (tx != ty) { printf("-1\n"); continue; } int ans = inf; if (dis[x] != dis[y]) { if (dis[x] < dis[y]) std :: swap(x, y); for (int j = 20; j >= 0; j--) { if (dis[jump[x][j]] > dis[y]) { ans = std :: min(ans, low[x][j]); x = jump[x][j]; } } ans = std :: min(ans, low[x][0]); x = jump[x][0]; } for (int j = 20; j >= 0; j--) { if (jump[x][j] != jump[y][j]) { ans = std :: min(ans, low[x][j]); ans = std :: min(ans, low[y][j]); x = jump[x][j]; y = jump[y][j]; } } if (x != y) { ans = std :: min(low[x][0], ans); ans = std :: min(low[y][0], ans); } printf("%d\n", ans); } return 0; }
相关文章推荐
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- 洛谷1967 火车运输 kruskal求最大生成树 倍增LCA维护最小值
- 洛谷P1967货车运输(最大生成树 && LCA倍增)
- 洛谷 P1967 货车运输(Kruskal最大生成树&&倍增lca)
- 洛谷 P1967 货车运输【NOIP2013D1T3】(最大生成树+倍增LCA)
- [NOIP2013]货车运输 D1 T3 最大生成树 LCA及其维护
- 洛谷.4180.[模板]次小生成树Tree(Kruskal LCA 倍增)
- noip2013 货车运输 (求解生成树路径上的最短边:倍增求最近共祖先+最大生成树Kruskal)