题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6705
题目大意:
给你一个有向图,任意一个点和边都可以经过很多次,问你整个图中,任意起点终点的k短路的长度是多少?你需要回答q个询问,每个询问给一个k
思路:
优先队列优化搜索,每次将所有点都加入队列中,意即每个点都可以为起点。那么,如果我们真正暴力地去搜索整个图,也就是每搜到一个点就把它所有相邻的点都加入优先队列中,那很明显会喜提TLE,只要将图变得很稠密,算法就会变为O(n^2)的规模。
这里有一个比较好想的优化,对每个点出度的边按照权值从小到大排序,如果我们每次按照最小的边扩张,那就是求最短路的方式。
那么k短路的情况有哪些呢?假设当前遍历的边为e,之前的点为e.from,现在的点为e.to,那么有两种情况可能更新答案:
第一,从e.from这个点出发,不经过e,而是通过from的另一条出边,这样有可能更新答案。
第二,从e.from出发,经过了e,到达了点e.to,选择e.to的一条出边,这样有可能更新答案。
所以,对于访问的一个点,只会有一条边进入优先队列中,再暴力地找就可以实现log的复杂度了。
在具体实现的时候,优先队列维护条边当前的出点,和这条边的编号,这样可以更方便的更新答案。
#pragma GCC optimize ("O2")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,int> pli;
const int MAXV=5e4+5,MAXE=5e4+5;
int V,E,Q;
ll ans[MAXV];
pii query[MAXV];
namespace G
{
struct Edge
{
int to, last, l;
void set(int _to, int _last, int _l) { to = _to, last = _last, l = _l; }
} es[MAXE]; // 边记得开两倍!!!
int top, head[MAXV];
void init() { top = 0, fill(head + 1, head + V + 1, -1); }
inline void add(int fr, int to, int l)
{
es[top].set(to, head[fr], l);
head[fr] = top++;
}
void sort_edge()
{
priority_queue<pii,vector<pii>,greater<pii> > q;
for(int i=1;i<=V;++i)
{
for(int j=head[i];~j;j=es[j].last) q.push(pii(es[j].l,es[j].to));
for(int j=head[i];~j;j=es[j].last) tie(es[j].l,es[j].to)=q.top(),q.pop();
}
}
void work()
{
priority_queue<pli,vector<pli>,greater<pli>> q;
for(int i=1;i<=V;++i)
if(~head[i]) q.push(pli(es[head[i]].l,head[i]));
int pos1=0,pos2=0,cur,last;
ll dis;
while(pos2<Q)
{
tie(dis,cur)=q.top(),q.pop();
++pos1;
while(pos2<Q && pos1==query[pos2].first)
ans[query[pos2].second]=dis,++pos2;
last=es[cur].last;
if(~last) q.push(pli(dis-es[cur].l+es[last].l,last)); // 加入出点相同的下一条边
last=head[es[cur].to];
if(~last) q.push(pli(dis+es[last].l,last)); // 加入从入点出去的第一条边
}
}
}
namespace io {
const int SIZE = 1e7 + 10;
char inbuff[SIZE];
char *l, *r;
inline void init() {
l = inbuff;
r = inbuff + fread(inbuff, 1, SIZE, stdin);
}
inline char gc() {
if (l == r) init();
return (l != r) ? *(l++) : EOF;
}
void read(int &x) {
x = 0; char ch = gc();
while(!isdigit(ch)) ch = gc();
while(isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
}
} using io::read;
int main()
{
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
int T;
read(T);
while(T--)
{
read(V),read(E),read(Q);
G::init();
int fr,to,l;
for(int i=0;i<E;++i)
{
read(fr),read(to),read(l);
G::add(fr,to,l);
}
G::sort_edge();
for(int i=0;i<Q;++i)
{
read(query[i].first);
query[i].second=i;
}
sort(query,query+Q);
G::work();
for(int i=0;i<Q;++i) printf("%lld\n",ans[i]);
}
return 0;
}