题意:给你一张有向加权图,n个节点,m条边,有Q次查询xi,输出图中,第xi小的路的权值。
思路:用vector<pi>保存每个点出度所连着的点和边权值,用一个改变优先级的优先队列保存:以u为终点,走过的路的权值和。对于Q次查询,我们找出最大的xi,记为mx,求出第1-第mx大的所有路径保存在ans中。如何求?取出优先队列的top(当前最短路径),枚举终点出度所连的信息,把新的路径长度(优先队列中的值 + 枚举的边权值)插入到multiset中。当set中元素个数等于mx时,还要继续枚举,直到set中最大的元素比当前路径长度小,原因请仔细思考。
#include <bits/stdc++.h>
#define pi pair<int,int>
#define mk make_pair
#define ll long long
using namespace std;
const int maxn = 5e4+10;
struct node {
int u;
ll dist;
bool operator < (const node& t)const {
return dist > t.dist;
}
};
priority_queue<node>q;
vector<pi>G[maxn];
multiset<ll>s;
int qr[maxn];
ll ans[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,m,Q,u,v,w,mx = 0;
scanf("%d%d%d",&n,&m,&Q);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
G[u].push_back(mk(w,v));
q.push(node{v,w});
s.insert(w);
}
for(int i=1;i<=n;i++)
sort(G[i].begin(),G[i].end());
for(int i=1;i<=Q;i++)
{
scanf("%d",&qr[i]);
mx = max(qr[i],mx);
}
int cnt = 0;
while(cnt < mx)
{
node tmp = q.top();
q.pop();
ans[++cnt] = tmp.dist;
u = tmp.u;
for(auto it : G[u])
{
v = it.second;
ll ww = tmp.dist + it.first;
if(s.size() == mx)
{
auto p = --s.end();
if(*p <= ww)break;
s.erase(p);
s.insert(ww);
}
else s.insert(ww);
q.push(node{v,ww});
}
}
for(int i=1;i<=Q;i++)printf("%lld\n",ans[qr[i]]);
s.clear();
for(int i=1;i<=n;i++)G[i].clear();
while(!q.empty())q.pop();
}
return 0;
}