题目:
题解:
本来以为是一道sb题然后WA过了才知道是大神题。。。
到达一个点有三种走法(窝一开始以为是两种)
1、一步步走a到达
2、当b < 2a,可以把两条a缩成一条b,如果是奇数就先走a
3、当b < a,这是一种很奇怪的走法,可以通过走更多的边数来换取较少的距离
比如这个图,到达A可以走a+b,也可以走2b
我们首先考虑暴力,就是当枚举到一个点,首先一边遍历与他相邻的点,对于这些点再二次遍历相邻的点,这次相邻的点可以用这个枚举点+b的长度更新
当然这样是过不去的,我们还需要优化。
首先每个点只会被更新一次,只有被更新点才有机会继续更新别的点。被更新一次是必然的,不然形成一个更新环就GG了
然后对于一组点A-x-y,可能会经历这样的过程,更新了y之后y入队,y反向边找到x,x又找到y,诚然这不会让答案错误,但是速度会慢,M^2的效率,我们可以考虑在更新的y之后把x-y这条边删掉,这样就不会进一步无用枚举了
代码:
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N=200005;
#define INF 1e9
int tot,nxt[N*2],point[N],v[N*2],dis[N],deep[N],ans[N],d[N];bool vis[N];
int tot1,nxt1[N*2],point1[N],v1[N*2],pre[N*2];
void addline(int x,int y)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void addline1(int x,int y)
{
++tot1; nxt1[tot1]=point1[x]; pre[point1[x]]=tot1; point1[x]=tot1; v1[tot1]=y;
++tot1; nxt1[tot1]=point1[y]; pre[point1[y]]=tot1; point1[y]=tot1; v1[tot1]=x;
}
void del(int x,int i)
{
if(i==point1[x]) point1[x]=nxt1[i];
nxt1[pre[i]]=nxt1[i];
pre[nxt1[i]]=pre[i];
}
int main()
{
int n,m,k,a,b;
scanf("%d%d%d%d%d",&n,&m,&k,&a,&b);
for (int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
addline(x,y);addline1(x,y);
}
memset(deep,0x7f,sizeof(deep));
deep[k]=0;memset(vis,0,sizeof(vis));
queue<int>q;q.push(k);int maxx=0;
while (!q.empty())
{
int now=q.front(); q.pop(); vis[now]=0;
for (int i=point[now];i;i=nxt[i])
if (deep[v[i]]>deep[now]+1)
{
deep[v[i]]=deep[now]+1;
maxx=max(maxx,deep[v[i]]);
if (!vis[v[i]]) vis[v[i]]=1,q.push(v[i]);
}
}
int base=min(2*a,b);
for (int i=1;i<=maxx;i++)
dis[i]=(i/2)*base+(i%2?a:0);
for (int i=1;i<=n;i++)
if (deep[i]<INF) ans[i]=dis[deep[i]];
if (b<a)
{
q.push(k);
while (!q.empty())
{
int now=q.front(); q.pop();
for (int i=point[now];i;i=nxt[i]) vis[v[i]]=1;
for (int i=point[now];i;i=nxt[i])
for (int j=point1[v[i]];j;j=nxt1[j])
{
if (d[v1[j]] || v1[j]==k || vis[v1[j]]) continue;
d[v1[j]]=d[now]+1;del(v[i],j);
q.push(v1[j]);
}
for (int i=point[now];i;i=nxt[i]) vis[v[i]]=0;
}
}
for (int i=1;i<=n;i++)
if (d[i]) ans[i]=min(ans[i],d[i]*b);
for (int i=1;i<=n;i++)
printf("%d\n",ans[i]);
}