2019CCPC网络预选赛 1004 path 最短路
题意:给你一张n个点m条边的有向图,问这张有向图的所有路径中第k短的路径长度是多少?n, m, k均为5e4级别。
思路:前些日子有一场div3的F和这个题有点像,但是那个题要求的是最短路,并且k最大只有400。这个题的做法其实是一个套路(没见过QAQ)。
首先把每个点的出边按边权从小到大排序,把每个点边权最小的那条边放入优先队列。对于优先队列中的每个点,记录一下这个点是从哪个点转移过来(last),转移过来的边对于last来说是第几小(rank)。这时,从当前点now选一条边权最小的边,形成新了路径放入队列。把last点的第k + 1小的边和last形成的新路径放入队列。第k次出队的路径就是第k短了路径。直观感受上是对的,证明的话纸上画画可能就出来了吧QAQ。
代码:
#include <bits/stdc++.h> #define LL long long #define pli pair<LL, int> #define pii pair<int, int> using namespace std; const int maxn = 100010; vector<pli> G[maxn]; void add(int x, int y, long long z) { G[x].push_back(make_pair(z, y)); } struct edge { int u, v; LL w; bool operator < (const edge& rhs) const { return w < rhs.w; } }; edge a[maxn]; LL ans[maxn]; int b[maxn]; struct node { int last, now, rank; LL dis; bool operator < (const node& rhs) const { return dis > rhs.dis; } }; priority_queue<node> q; int main() { int T, n, m, t; scanf("%d", &T); while(T--) { while(q.size()) q.pop(); scanf("%d%d%d", &n, &m, &t); for (int i = 1; i <= n; i++) G[i].clear(); for (int i = 1; i <= m; i++) { scanf("%d%d%lld", &a[i].u, &a[i].v, &a[i].w); add(a[i].u, a[i].v, a[i].w); } int lim = 0; for (int i = 1; i <= t; i++){ scanf("%d", &b[i]); lim = max(lim, b[i]); } for (int i = 1; i <= n; i++) { sort(G[i].begin(), G[i].end()); } for (int i = 1; i <= n; i++) { if(G[i].size()) { q.push((node){i, G[i][0].second, 0, G[i][0].first}); } } for (int i = 1; i <= lim; i++) { node tmp = q.top(); q.pop(); ans[i] = tmp.dis; if(G[tmp.now].size()) q.push((node){tmp.now, G[tmp.now][0].second, 0, tmp.dis + G[tmp.now][0].first}); if(G[tmp.last].size() > tmp.rank + 1) { q.push((node){tmp.last, G[tmp.last][tmp.rank + 1].second, tmp.rank + 1, tmp.dis - G[tmp.last][tmp.rank].first + G[tmp.last][tmp.rank + 1].first}); } } for (int i = 1; i <= t; i++) { printf("%lld\n", ans[b[i]]); } } }