NOIP模拟 有向无环图(玄学建图+最短路)
内网传送门
【题目分析】
SPFA竟然有人乱搞A了?orz(蒟蒻乱搞只有40pts qwq)
很巧妙的建图思路,将每条路径视为一个点,从一条路径i到达另一条路径j,如果w[i]<w[j],那么会产生w[j]-w[i]的费用,否则不会产生任何费用。
所以考虑将所有边存下,然后每个点遍历一遍,将边排序,从小到大依次加费用为0的边,再从大到小加费用为w[i]-w[i-1]的边,最后在图上跑迪杰斯特拉即可。
【代码~】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int> pii;
const int MAXN=2e5+50;
const int MAXM=4e5+50;
int n,m,ecnt=1;
int head[MAXN],vis[MAXM],tot;
int nxt[MAXM],to[MAXM],w[MAXM];
ll dis[MAXM];
vector<pii>edge[MAXM];
priority_queue< pii,vector<pii>,greater<pii> >q;
struct Edge{
int id,w;
Edge(){}
Edge(int x,int z):id(x),w(z){}
friend inline bool operator <(const Edge &a,const Edge &b){
return a.w<b.w;
}
}e[MAXM];
int Read(){
int i=0,f=1;
char c;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
void add(int x,int y,int z){
nxt[++ecnt]=head[x];
head[x]=ecnt;
to[ecnt]=y;
w[ecnt]=z;
}
void dijkstra(){
for(int i=2;i<=ecnt;i++)
dis[i]=2e18;
for(int i=head[1];i;i=nxt[i]){
dis[i]=w[i];
q.push(make_pair(dis[i],i));
}
while(!q.empty()){
if(vis[q.top().second]){
q.pop();
continue;
}
int u=q.top().second;
q.pop();
vis[u]=1;
for(int i=edge[u].size()-1;i>=0;i--){
int v=edge[u][i].first;
int w=edge[u][i].second;
if(vis[v])
continue;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
q.push(make_pair(dis[v],v));
}
}
}
}
int main(){
n=Read(),m=Read();
for(int i=1;i<=m;i++){
int x=Read(),y=Read(),z=Read();
add(x,y,z);add(y,x,z);
}
for(int u=2;u<n;u++){
tot=0;
for(int i=head[u];i;i=nxt[i])
e[++tot]=Edge(i,w[i]);
sort(e+1,e+tot+1);
for(int i=1;i<=tot;i++){
if(i!=1)
edge[e[i].id].push_back(make_pair(e[i-1].id,0));
if(i!=tot)
edge[e[i].id].push_back(make_pair(e[i+1].id,e[i+1].w-e[i].w));
edge[e[i].id^1].push_back(make_pair(e[i].id,e[i].w));
}
}
dijkstra();
ll ans=2e18;
for(int i=head[n];i;i=nxt[i])
ans=min(ans,dis[i^1]+w[i]);
cout<<ans;
return 0;
}