NOIP 2013 Senior 3 - 货车运输
2017-07-20 11:19
267 查看
Description
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车 在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
Input
第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。 注意:x 不等于 y,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。
Output
输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。
Sample Input
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
Sample Output
3
-1
3
Data Constraint
对于 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。
这个题给你的第一感觉是什么?搜索?变形SPFA?很明显的是,哪怕这两个算法的时间复杂度都降低到接近O(n)了,也会超时,所以我们还是从数据规模入手。发现30%的数据和60%的数据很有玄机:只有边数的规模改变了,顶点和询问还是那么多。所以我们断定,这道题一定跟边的处理有关。
经过观察后发现,我们要求的路径是要求最短边最长,跟总长无关,也就是说每条边尽量长就可以了。考虑一个两点之间只有唯一路径的图,那它就是一棵树,最优答案就是唯一的答案。反过来,我们走出来的最优答案一定是一棵树。如果这棵树再加上几条边呢?就成了我们题目给的图了。因此,要让这棵树最优,那么这棵树的每条边就应该最优。所以这棵树是一棵最大生成树,因为不是最大生成树的树答案不比最大生成树的答案好。
好了,现在我们用Kruskal算法拿到了一棵(无根)树。那么要求两点之间路径怎么求呢?爆搜?= =很明显,通常做法都是把无根树转换为有根树,即先随便找一个点深搜一次。现在要求两点之间的路径就需要找最近公共祖先了。
可以保存每个点的depth,然后通过最近公共祖先的depth和当前点的depth计算出要走的步数,边走边统计。因此还可以使用倍增加速。至此,思路已经基本完善。
但是,题目给定的图可能包含多个互不连通的子图,因此,我们得到的是一个森林。所以在写代码的时候还要注意处理不在同一棵树上的情况,例如,要dfs所有点。
参考代码
写的比较复杂。要加强对基础算法的回顾,像这道题完全就是一堆算法模板拿来套。
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车 在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
Input
第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。 注意:x 不等于 y,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。
Output
输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。
Sample Input
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
Sample Output
3
-1
3
Data Constraint
对于 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。
这个题给你的第一感觉是什么?搜索?变形SPFA?很明显的是,哪怕这两个算法的时间复杂度都降低到接近O(n)了,也会超时,所以我们还是从数据规模入手。发现30%的数据和60%的数据很有玄机:只有边数的规模改变了,顶点和询问还是那么多。所以我们断定,这道题一定跟边的处理有关。
经过观察后发现,我们要求的路径是要求最短边最长,跟总长无关,也就是说每条边尽量长就可以了。考虑一个两点之间只有唯一路径的图,那它就是一棵树,最优答案就是唯一的答案。反过来,我们走出来的最优答案一定是一棵树。如果这棵树再加上几条边呢?就成了我们题目给的图了。因此,要让这棵树最优,那么这棵树的每条边就应该最优。所以这棵树是一棵最大生成树,因为不是最大生成树的树答案不比最大生成树的答案好。
好了,现在我们用Kruskal算法拿到了一棵(无根)树。那么要求两点之间路径怎么求呢?爆搜?= =很明显,通常做法都是把无根树转换为有根树,即先随便找一个点深搜一次。现在要求两点之间的路径就需要找最近公共祖先了。
可以保存每个点的depth,然后通过最近公共祖先的depth和当前点的depth计算出要走的步数,边走边统计。因此还可以使用倍增加速。至此,思路已经基本完善。
但是,题目给定的图可能包含多个互不连通的子图,因此,我们得到的是一个森林。所以在写代码的时候还要注意处理不在同一棵树上的情况,例如,要dfs所有点。
参考代码
#include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <iostream> #include <algorithm> #include <vector> #include <string> #include <stack> #include <queue> #include <deque> #include <map> #include <set> #include <bitset> using std::cin; using std::cout; using std::endl; typedef int INT; inline INT readIn() { bool minus = false; INT a = 0; char ch = getchar(); while (!(ch == '-' || ch >= '0' && ch <= '9')) ch = getchar(); if (ch == '-') { minus = true; ch = getchar(); } while (ch >= '0' && ch <= '9') { a *= 10; a += ch; a -= '0'; ch = getchar(); } if (minus) a = -a; return a; } struct DS { std::vector<INT> parent; DS(INT size) : parent(size + 1) { for (int i = 1; i <= size; i++) { parent[i] = i; } } INT find(INT x) { if (parent[x] == x) return x; return parent[x] = find(parent[x]); } INT unite(INT x, INT y) { INT px = find(x); INT py = find(y); parent[py] = px; } INT judge(INT x, INT y) { return find(x) == find(y); } }; const INT maxn = 10005; INT n, m; struct E { INT from; INT to; INT cost; E(INT from = 0, INT to = 0, INT cost = 0) : from(from), to(to), cost(cost) { } bool operator< (const E& b) const { return cost > b.cost; } } e[50005]; INT k; struct edge { INT to; INT cost; edge(INT to = 0, INT cost = 0) : to(to), cost(cost) { } }; std::vector<std::vector<edge> > edges; void kruskal(DS &ds) { edges.resize(n + 1); INT count_ = 0; for (int i = 0; i < m; i++) { if (!ds.judge(e[i].from, e[i].to)) { ds.unite(e[i].from, e[i].to); edges[e[i].from].push_back(edge(e[i].to, e[i].cost)); edges[e[i].to].push_back(edge(e[i].from, e[i].cost)); } } } std::vector<INT> sequence(1); std::vector<INT> vis; std::vector<INT> depth; std::vector<std::vector<INT> > stTable(1); std::vector<std::vector<INT> > parent(1); std::vector<std::vector<INT> > minLen(1); void dfs(INT node = 1) { vis[node] = sequence.size(); sequence.push_back(node); for (int i = 0; i < edges[node].size(); i++) { INT to = edges[node][i].to; if (to == parent[0][node]) continue; INT cost = edges[node][i].cost; depth[to] = depth[node] + 1; parent[0][to] = node; minLen[0][to] = cost; dfs(to); sequence.push_back(node); } } void ST() { INT N = sequence.size() - 1; INT size = 0; while (1 << (size + 1) <= N) size++; stTable[0].resize(N + 1); for (int i = 1; i <= N; i++) { stTable[0][i] = sequence[i]; } for (int i = 1; i <= size; i++) { stTable.push_back(std::vector<INT>()); stTable.back().resize(N + 1); for (int j = 1; j + (1 << i) - 1 <= N; j++) { INT x = stTable[i - 1][j]; INT y = stTable[i - 1][j + (1 << (i - 1))]; if (depth[stTable[i - 1][j]] < depth[stTable[i - 1][j + (1 << (i - 1))]]) { stTable[i][j] = stTable[i - 1][j]; } else { stTable[i][j] = stTable[i - 1][j + (1 << (i - 1))]; } } } N = n; size = 0; while (1 << (size + 1) <= N) size++; for (int i = 1; i <= size; i++) { parent.push_back(std::vector<INT>()); minLen.push_back(std::vector<INT>()); parent.back().resize(N + 1); minLen.back().resize(N + 1); for (int j = 1; j <= N; j++) { parent[i][j] = parent[i - 1][parent[i - 1][j]]; if (parent[i][j]) minLen[i][j] = std::min(minLen[i - 1][j], minLen[i - 1][parent[i - 1][j]]); } } } INT getMin(int& x, int& y) { INT l = vis[x]; INT r = vis[y]; if (l > r) { std::swap(l, r); std::swap(x, y); } INT length = r - l + 1; INT size = 0; while (1 << (size + 1) <= length) size++; if (depth[stTable[size][l]] < depth[stTable[size][r - (1 << size) + 1]]) return stTable[size][l]; else return stTable[size][r - (1 << size) + 1]; } void run() { n = readIn(); m = readIn(); for (int i = 0; i < m; i++) { e[i].from = readIn(); e[i].to = readIn(); e[i].cost = readIn(); } std::sort(e, e + m); DS ds(n); kruskal(ds); vis.resize(n + 1); depth.resize(n + 1); parent[0].resize(n + 1); minLen[0].resize(n + 1); for (int i = 1; i <= n; i++) { if (!vis[i]) dfs(i); } ST(); k = readIn(); while (k--) { INT from = readIn(); INT to = readIn(); if (ds.judge(from, to)) { INT anc = getMin(from, to); INT depthFrom = depth[from]; INT depthTo = depth[to]; INT depthAnc = depth[anc]; INT stepFrom = depthFrom - depthAnc; INT stepTo = depthTo - depthAnc; INT minVal = 0x7ffffff; for (int i = parent.size() - 1; ~i; i--) { if (stepFrom & (1 << i)) { minVal = std::min(minVal, minLen[i][from]); from = parent[i][from]; } } for (int i = parent.size() - 1; ~i; i--) { if (stepTo & (1 << i)) { minVal = std::min(minVal, minLen[i][to]); to = parent[i][to]; } } printf("%d\n", minVal); } else { printf("-1\n"); } } } int main() { run(); return 0; }
写的比较复杂。要加强对基础算法的回顾,像这道题完全就是一堆算法模板拿来套。
相关文章推荐
- NOIp2013 货车运输 By cellur925
- 【NOIP2013】货车运输
- 【最大生成树+倍增】NOIP 2013 货车运输
- [NOIP2013][CODEVS3287]货车运输(kruskal+树上倍增)
- NOIP 2013 【货车运输】
- [noip2013] 货车运输(最大生成树+并查集+LCA)
- 【NOIP2013】货车运输
- NOIP 2013 CODE[VS] 3287 货车运输 倍增LCA + 最大生成树
- NOIP2013货车运输(最大生成树+LCA)
- NOIP2013复赛提高组day1(A:转圈游戏 B:火柴排队 C:货车运输)
- 【CJOJ1090】【洛谷1967】【NOIP2013】货车运输
- 【noip2013提高组day1T3】 货车运输 RMQ+LCA+倍增+最大生成树
- NOIP2013 D1T3 货车运输 倍增LCA OR 并查集按秩合并
- [NOIp 2013]货车运输
- [noip2013]货车运输
- 洛谷 P1967 Vijos P1843 CODE[VS] P3287 [NOIP2013 D1T3] 货车运输
- NOIP2013 货车运输 (最大生成树+树上倍增LCA)
- NOIP 2013 Day1 T3 货车运输
- [NOIP2013]货车运输,最大生成树+LCA
- [NOIP2013] 货车运输 最大生成树 LCA