[noip2013]货车运输(kruskal + 树上倍增)
2015-08-18 09:15
344 查看
描述
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。格式
输入格式
第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。
输出格式
输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。样例1
样例输入1
4 3 1 2 4 2 3 3 3 1 1 3 1 3 1 4 1 3
样例输出1
3 -1 3
限制
每个测试点1s。提示
对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q < 1,000;对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q < 1,000;
对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q < 30,000,0 ≤ z ≤ 100,000。
因为货车要运输最大货物,必须在整张图的最大生成树上选边,可以想到kruskal算法,但此时若要对每一个询问进行一次遍历的话需要 O(mn) 的时间复杂度,那么就需要把答案预先处理出来。
RMQ问题的ST算法 是用 d(i, j) 表示 从 i 开始 长度为 2^j 的一段元素之间的最小值
那么 d(i, j) = min{d(i ,j - 1), d(i + 2 ^ (j - 1), j - 1)}; 从 i 开始长度为 2^j 的最小值 = min{从 i 开始长度为2^(j - 1) 的一段元素的最小值, 从i + 2^(j - 1) + 1开始长度为2^(j - 1)的最小值};
这个题在处理时同样可以借鉴这种思路,这个题在树上(我也不知道在说啥),所以:
以 f[i][j] 表示第i个结点的第2^j个祖先是哪个点,那么 f[i][0] 为第i个节点的父亲。
以 g[i][j] 表示第i个结点到 f[i][j](第i个结点的第2^j个祖先) 的最短距离,那么 g[i][0] 表示第i个节点到自己父亲的(最短)距离。
转移可以模仿ST算法:
f[i][j] = f[ f[i][j - 1] ][j - 1]; 第i个点的第2^j个祖先 = 【第i个点第2^(j - 1)的祖先】 的 【第2^(j - 1)的祖先】
g[i][j] = min(g[i][j - 1], g[ f[i][j - 1] ][j - 1]); 第i个结点到 自己的第2^j个祖先 的最短距离 = min{第i个结点到 自己的第 2^(j - 1) 个祖先 的最短距离,【 第i个结点的第2^(j - 1)个祖先】到 【自己的第2^(j - 1)个祖先】 的最短距离}
【】只是为了帮助断句。。。
之后若要查询(x, y)的最短距离只需查询 min{(x, pos)的最短距离,(y, pos)的最短距离} pos为x,y的最近公共祖先
那么借用dfs的思路即可预处理整张图得到f和g
#include <stdio.h> #include <string.h> #include <math.h> #include <algorithm> using namespace std; struct node { int x, y, val; }edge[50005]; int n, m, cnt; bool cmp(node a, node b) { return a.val > b.val; } int u[50005], v[50005], next[50005], first[10005], w[50005]; int in[10005], father[10005], hi[10005]; void addedge(int st, int end, int value) { u[++cnt] = st, v[cnt] = end, w[cnt] = value; next[cnt] = first[st]; first[st] = cnt; } int getfather(int x) { return father[x] == x ? x : father[x] = getfather(father[x]); } void kruskal() { int count; for (int i = 1; i <= n; i++) father[i] = i; for (int i = 1; i <= m; i++) { int st = edge[i].x, end = edge[i].y; int fa = getfather(father[st]), fb = getfather(father[end]); if (fa != fb) { addedge(st, end, edge[i].val); addedge(end, st, edge[i].val); father[fb] = fa; count++; if (count == n - 1) return; } } } int vis[10005]; int fa[10005][25], Min[10005][25]; void dfs(int o) { vis[o] = 1; for (int i = 1; i <= 16; i++) { if (hi[o] < (1 << i)) break; fa[o][i] = fa[fa[o][i - 1]][i - 1]; Min[o][i] = min(Min[o][i - 1], Min[fa[o][i - 1]][i - 1]); } for (int i = first[o]; i; i = next[i]) { int end = v[i]; if (!vis[end]) { fa[end][0] = o; Min[end][0] = w[i]; hi[end] = hi[o] + 1; dfs(end); } } } int lca(int l, int r) { if (hi[l] < hi[r]) swap(l, r); int t = hi[l] - hi[r]; for (int i = 0; i <= 16; i++) { if ((1 << i) & t) l = fa[l][i]; } for (int i = 16; i >= 0; i--) { if (fa[l][i] != fa[r][i]) { l = fa[l][i], r = fa[r][i]; } } if (l == r) return l; return fa[l][0]; } int ask(int l, int r) { int ret = 0x3f3f3f3f; int t = hi[l] - hi[r]; for (int i = 0; i <= 16; i++) { if (t & (1 << i)) { ret = min(ret, Min[l][i]); l = fa[l][i]; } } return ret; } int main() { memset(Min, 0x3f3f3f3f, sizeof(Min)); scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) { scanf("%d%d%d", &edge[i].x, &edge[i].y, &edge[i].val); } sort(edge + 1, edge + 1 + m, cmp); kruskal(); for (int i = 1; i <= n; i++) if (!vis[i]) dfs(i); int t, l, r; scanf("%d", &t); while (t--) { scanf("%d%d", &l, &r); if (getfather(father[l]) != getfather(father[r])) printf("-1\n"); else { int pos = lca(l, r); printf("%d\n", min(ask(l, pos), ask(r, pos))); } } return 0; }
变量名不好起啊。
相关文章推荐
- [Leetcode] Linked List Cycle II
- 面向对象和面向过程
- 串口之COMMTIMEOUTS结构体详解
- hdu2454 Degree Sequence of Graph G(havel定理)
- HDU 4513 吉哥系列故事——完美队形II(Manacher算法最大回文长度 && 两侧沿中点递减)
- browsers shortcut
- 算法竞赛入门经典:第七章 暴力求解法 7.5枚举排列
- Java对象的强、软、弱和虚引用原理+结合ReferenceQueue对象构造Java对象的高速缓存器
- 串口之COMMTIMEOUTS结构体详解
- [转]Window 通过cmd查看端口占用、相应进程、杀死进程等的命令
- SPOJ:REPEATS - Repeats
- 黑马程序员_面向对象(1)
- 归并排序
- Android笔记
- javaweb 同名用户登录剔除退出功能
- iOS UI14_GET-POST
- NYOJ 746 整数划分(四)
- hdu1285
- 一个大浪Java罢工(一个)安装JDK和环境变量配置
- vi常用快捷键