题解:P15716 [JAG 2023 Summer Camp #2] Drifting
Austin0116 · · 题解
分析
要求最短路,首先我们注意到这是一个 DAG,然后我们自然想到类似拓扑地解决。由于整张图只有一个转移方向,所以我们直接 DP,等到一个点所有边都被松弛了再让它加到队列中,每条边只会被松弛一次,所以时间复杂度是正确的。
实现有比较多的细节。(当然也有可能是我写得比较丑,具体看代码)
代码
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <map>
#include <cstring>
#define getchar getchar_unlocked
#define N 200005
#define ll long long
using namespace std;
int n,mm,u,v,w,h[N],t,k,cnt,in[N];
vector<int> b,c[N],y;
map<pair<int,int>,int> m;
struct Ed{
int v,nxt,w;
};
Ed e[N<<1];
inline void add(int u,int v,int w){
e[++t]=Ed{v,h[u],w};
h[u]=t;
}
struct ha{
int u,v;
ll w;
inline bool operator < (const ha &o) const{
return w<o.w;
}
};
vector<ha> d[N];
queue<ha> q;
inline void topo(){
for(int i=1;i<=n;i++) if(!in[i]) q.push({0,i,0});
q.pop();
while(!q.empty()){
ha u=q.front();
q.pop();
for(int i=h[u.v];~i;i=e[i].nxt){
in[e[i].v]--;
if(!in[e[i].v]) q.push({0,e[i].v,0});
}
} //将除 1 之外的入度为 0 的边先把点的入度减去,因为无贡献
q.push({0,1,0});
b.push_back(1);
while(1){
while(!q.empty()){
ha u=q.front();q.pop();
d[u.v].push_back({u.u,u.v,u.w});
}
if(b.empty()) break;
for(int u:b){
sort(d[u].begin(),d[u].end());
for(int i=h[u];~i;i=e[i].nxt) if(!(--in[e[i].v])) y.push_back(e[i].v);//将入度删到 0 的边准备入队
for(ha x:d[u]) if(~h[u]){
int *pre=&h[u];
bool fl=m.count({x.u,x.v});
int p;
if(fl) p=m[{x.u,x.v}];
for(int i=h[u];~i;i=e[i].nxt){
if(fl&&binary_search(c[p].begin(),c[p].end(),e[i].v)){
*pre=i;
pre=&e[i].nxt;
}//链式前向星,用链表快速删边
else q.push({u,e[i].v,x.w+e[i].w});
}
*pre=-1;
}
}
b.clear();
swap(b,y);
}
}
inline void read(int &x){
x=0;
char ch=getchar();
while(ch>57||ch<48) ch=getchar();
while(ch>=48&&ch<=57){
x=x*10+(ch^48);
ch=getchar();
}
}
int main(){
memset(h,-1,sizeof(h));
read(n),read(mm);
for(int i=1;i<=mm;i++){
read(u),read(v),read(w);
add(u,v,w);
in[v]++;
}
read(k);
for(int i=1;i<=k;i++){
read(u),read(v),read(w);
if(!m.count({u,v})) m[{u,v}]=++cnt;
c[m[{u,v}]].push_back(w);//处理边
}
for(int i=1;i<=cnt;i++) sort(c[i].begin(),c[i].end());
topo();
sort(d[n].begin(),d[n].end());
if(d[n].empty()) puts("-1");
else printf("%lld",d[n][0].w);
return 0;
}