Description
异变又发生了,魂魄妖梦作为幻想乡的一名自(cheng)机(guan),主动前去解决异变。
我们用一个n个点、m条边的无向联通图来表示妖梦可选择的路线,妖梦从白玉楼出发,白玉楼被视为编号为1的点,编号为2——n的点是幻想乡的村庄,其中编号为n的村庄发生了异变。
每条边上可能会有一些妖怪袭击人类(然而妖梦是半人半灵),所以对于第i条边,妖梦需要t[i]分钟通过这条路。妖梦带了她的人符[现世斩],可以使所有连接点x的边的通过时间变成1(x可以任意指定)。然而为了保留足够的力量解决异变,妖梦只会用这个符卡一次。妖梦想知道,她到达村庄n的最短时间是多少。
100%:2≤n≤100,000 n-1≤m≤500,000 1≤t[i]≤1,000,000,000
可能有重边和自环,保证能从1到n
Analysis
可以分层跑,跟那道GDOI2016day2T1,基本一样。
或者,有一种更机智的方法。从1和n各跑一次最短路,然后枚举一个中间点被废掉,由于中间点所连出的边被废掉都会变成1,所以再枚举中间点连出去的点u,更新min(dis[1~u])和min(dis[u~n]),取到两个min再加上2.
Code
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define efo(i,v) for(int i=last[v];i;i=next[i])
using namespace std;
typedef long long ll;
const int N=100010,M=1000010;
const ll INF=2147483647000000;
int n,m,tot,to[M],next[M],last[N];
ll wei[M],dis[2][N];
queue<int> q;
bool bz[N];
void link(int u,int v,int w)
{
to[++tot]=v,wei[tot]=w,next[tot]=last[u],last[u]=tot;
}
void spfa(int S,int p)
{
fill(dis[p],dis[p]+N,INF);
dis[p][S]=0;
q.push(S);
memset(bz,0,sizeof(bz));
bz[S]=1;
while(!q.empty())
{
int u=q.front();q.pop();
bz[u]=0;
efo(i,u)
{
int v=to[i];
if(dis[p][u]+wei[i]<dis[p][v])
{
dis[p][v]=dis[p][u]+wei[i];
if(!bz[v])
{
bz[v]=1;
q.push(v);
}
}
}
}
}
int main()
{
freopen("cut.in","r",stdin);
freopen("cut.out","w",stdout);
int u,v,w;
scanf("%d %d",&n,&m);
tot=1;
fo(i,1,m)
{
scanf("%d %d %d",&u,&v,&w);
if(u==v) continue;
link(u,v,w),link(v,u,w);
}
spfa(1,0);
spfa(n,1);
ll ans=INF;
fo(v,1,n)
{
ll l=INF,r=INF;
efo(i,v)
{
int u=to[i];
l=min(l,dis[0][u]);
r=min(r,dis[1][u]);
}
ans=min(ans,l+r+2);
}
printf("%lld\n",ans);
return 0;
}