题解:
由于(2≤n≤105,0≤m≤5⋅105,1≤a,b≤109),且题目分为两种边,其实即为一张图和其补图,图上为m条边且边长为a,补图的边很大,所以关键在于如何遍历补图。由于我们只需要找出一条最短路,且每个点最多遍历一次,所以可以用set来扩展边,可以极大的缩小复杂度。
AC code
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <iostream>
#include <cstring>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5 + 10;
const int maxm = 5e5 + 10;
int n,m,a,b;
set <int> cnt[maxn];
vector <int> G[maxn];
int vis[maxn];
int bfs1()
{
queue<int> q;
q.push(1);
vis[1] = 0;
while(!q.empty())
{
int t = q.front();
if(t == n) return vis[n];
q.pop();
for(int i = 0; i < G[t].size(); i++)
{
if(vis[G[t][i]] == -1)
{
vis[G[t][i]] = vis[t] + 1;
if(G[t][i] == n) return vis[n];
q.push(G[t][i]);
}
}
}
return vis[n];
}
int bfs2()
{
queue<int> q;
q.push(1);
vis[1] = 0;
set<int> cntn;
for(int i = 2; i <= n; i++) cntn.insert(i);
while(!q.empty())
{
int t = q.front();
q.pop();
if(t == n) return vis[n];
int v = -1;
while(true)
{
set <int> :: iterator iter = cntn.upper_bound(v);
if(iter == cntn.end()) break;
v = *iter;
if(cnt[t].count(v) == 0)
{
vis[v] = vis[t] + 1;
if(v == n) return vis[n];
q.push(v);
cntn.erase(iter);
}
}
}
return vis[n];
}
int main()
{
while(~scanf("%d%d%d%d",&n,&m,&a,&b))
{
int u,v,ans;
for(int i = 0; i <= n; i++) {cnt[i].clear();G[i].clear();vis[i] = -1;}
for(int i = 0; i < m; i++)
{
scanf("%d%d",&u,&v);
cnt[u].insert(v);
cnt[v].insert(u);
G[u].push_back(v);
G[v].push_back(u);
}
if(cnt[1].count(n) == 0)
{
if(a >= b) {printf("%d\n",b); continue;}
int x = bfs1();
if(x != -1) ans = min(b,x*a);
else ans = b;
}
for(int i = 0; i <= n; i++) vis[i] = -1;
if(cnt[1].count(n) == 1)
{
if(a <= b) {printf("%d\n",a); continue;}
int x = bfs2();
if(x != -1) ans = min(a,x*b);
else ans = a;
}
printf("%d\n",ans);
}
return 0;
}