首先我们发现因为每条边无限用,因此会有一种优先队列贪心的基本思路。只要每次弹出最小的路径就是答案。
但是如果每次都将所有边存入,取出最小边后又存入,这样会导致内存爆炸并且超时。
所以我们要想办法是否可以存一些必要的可能成为答案的边,首先我们将以i开始的边都存到对应的vector中
如果我们从优先队列中取出一条边,我们发现,可能成为答案的边除了本身就在队列中的边外,还有可能是以终点扩展出的最小边,以及以边长度在出发点出去的并且长度比这条边大的边。
每次只要这样维护即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=3e5+10; struct edge{ ll to,w; bool operator <(const edge& t)const{ return w<t.w; } }; struct node{ ll pre,to,num,w; bool operator <(const node&t) const{ return w>t.w; } }; vector<edge> g[N]; int k[N]; ll ans[N]; int main(){ ios::sync_with_stdio(false); int t; cin>>t; while(t--){ int n,m,Q; cin>>n>>m>>Q; int i; for(i=1;i<=n;i++) g[i].clear(); for(i=1;i<=m;i++){ int a,b,c; cin>>a>>b>>c; g[a].push_back({b,c}); } priority_queue<node> q; int mx=0; for(i=1;i<=Q;i++){ cin>>k[i]; mx=max(mx,k[i]); } int cnt=0; for(i=1;i<=n;i++){ sort(g[i].begin(),g[i].end()); if(g[i].size()) q.push({i,g[i][0].to,0,g[i][0].w}); } while(q.size()){ auto t=q.top(); q.pop(); cnt++; ans[cnt]=t.w; if(cnt==mx) break; if(t.num<(int)g[t.pre].size()-1){ q.push({t.pre,g[t.pre][t.num+1].to,t.num+1,t.w-g[t.pre][t.num].w+g[t.pre][t.num+1].w}); } if((int)g[t.to].size()){ q.push({t.to,g[t.to][0].to,0,t.w+g[t.to][0].w}); } } for(i=1;i<=Q;i++){ cout<<ans[k[i]]<<endl; } } }