CPCCamp 有 n 个地铁站,用 1,2,…,n 编号。 m 段双向的地铁线路连接 n 个地铁站,其中第 i 段地铁属于 ci 号线,位于站 ai,bi 之间,往返均需要花费 ti 分钟(即从 ai 到 bi 需要 ti 分钟,从 bi 到 ai 也需要 ti 分钟)。
众所周知,换乘线路很麻烦。如果乘坐第 i 段地铁来到地铁站 s,又乘坐第 j 段地铁离开地铁站 s,那么需要额外花费 |ci-cj | 分钟。注意,换乘只能在地铁站内进行。
Bobo 想知道从地铁站 1 到地铁站 n 所需要花费的最小时间。
解:对于每个一个地铁站的多个换乘入口拆点,内部的边是O(n)的,因为换车行所需要的时间是。
计算得出需要 最多建造 6*1e5的边 ( 原图 + 拆点最多贡献 2被 )*2 (其中外面乘二 是因为双向边)。
#include <bits/stdc++.h>
#define en '\n'
#define Swt signed main
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f3f3f3f3f;
const int maxn=1e5+10;
int rd(){int tem;scanf("%d",&tem);return tem;}
int n,m,tot,tot2,head[maxn<<1];
ll dis[maxn<<1];
bool vis[maxn<<1];
struct node{
int v,nxt,w;
}edge[maxn*6];
void add(int x,int y,int w){
edge[++tot2]=(node){y,head[x],w};head[x]=tot2;
}
vector<int>vec[maxn];
map<ll,int>ma[maxn];
priority_queue< pair<int,int> >q;
void dj(){
#define mem(a,b) memset(a,b,sizeof(a))
mem(dis,0x3f);
for(int j=0;j<vec[1].size();j++){dis[ma[1][vec[1][j]]]=0;q.push({0,ma[1][vec[1][j]]});}
while(q.size()){
int x=q.top().second;q.pop();
if(vis[x])continue;
vis[x]=1;
for(int i=head[x];i;i=edge[i].nxt){
int y=edge[i].v;
if(dis[y]>dis[x]+edge[i].w){
dis[y]=dis[x]+edge[i].w;
q.push({-dis[y],y});
}
}
}
}
Swt(){
#ifdef local
freopen("input2.txt","r",stdin);
#endif // local
while(cin>>n>>m){
tot=0;tot2=0;
mem(head,0);
for(int i=1;i<=n;i++){
ma[i].clear();vec[i].clear();
}
#define pb push_back
for(int i=1;i<=m;i++){
int a=rd(),b=rd(),c=rd(),t=rd();
if(!ma[a][c])ma[a][c]=++tot,vec[a].pb(c);
if(!ma[b][c])ma[b][c]=++tot,vec[b].pb(c);
add(ma[a][c],ma[b][c],t);
add(ma[b][c],ma[a][c],t);
}
for(int i=1;i<=n;i++){
sort(vec[i].begin(),vec[i].end());
if(vec[i].size()<1)continue;
for(int j=1;j<vec[i].size();j++){
int ind=ma[i][vec[i][j-1]],ind_=ma[i][vec[i][j]];
add(ind,ind_,abs(vec[i][j-1]-vec[i][j]));
add(ind_,ind,abs(vec[i][j-1]-vec[i][j]));
}
}
dj();
ll ans=inf;
for(int j=0;j<vec[n].size();j++){
ans=min(ans,dis[ma[n][vec[n][j]]]);
}cout<<ans<<en;
}
return 0;
}