題意:給你一張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]]);
}
}
}
