分层图板子题
题目描述
Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在(n)城市设有业务,设这些城市分别标记为 (0) 到 (n-1),一共有 (m) 种航线,每种航线连接两个城市,并且航线有一定的价格。
Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多 (k) 种航线上搭乘飞机。那么 Alice 和 Bob 这次出行最少花费多少?
输入格式
数据的第一行有三个整数,(n,m,k),分别表示城市数,航线数和免费乘坐次数。
第二行有两个整数,(s,t),分别表示他们出行的起点城市编号和终点城市编号。
接下来有 (m) 行,每行三个整数,(a,b,c),表示存在一种航线,能从城市 (a) 到达城市 (b),或从城市 (b) 到达城市 (a),价格为 (c)。
输出格式
输出一行一个整数,为最少花费。
建立 ((k+1)n) 个点,其中第 (i*n+j) 个表示的是已经使用了(i)次免费机会,目前在第 (j) 个实际节点的最短距离
这是一个分层的感觉,以只能用一次免费机会为例,没用过的是一张图,用过的是一张图,这每张图内部的边就按照题目信息连接就行
然后还有两张图之间的边,像这样,(u',v')指的是用过免费机会以后的那个图:
(u
ightarrow v',v
ightarrow u')的权值为(0),意思是花费(0)的代价可以用一次免费机会来走这些边
而在每张图内部的边权值是(len),意思时不花费免费机会需要用 (len) 的代价来走
然后跑一边 dij 就行,但是当他的终点是(end),实际路程要为:(min_{i=0}^k dis(i*n+end))
因为不一定每一次机会都要用
在我们构建的这张分层的图中,点数是 ((k+1)n),边数是 (m(2k+1))
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
int n,m,k;
int start,end;
int nex[2100006],to[2100006],w[2100006];
int fir[110006],tot;
int heap[110006],size;
int dis[110006],in[110006];
inline void add(int u,int v,int len){
to[++tot]=v;w[tot]=len;
nex[tot]=fir[u];fir[u]=tot;
}
inline void link(int u,int v,int len){
add(u,v,len);add(v,u,len);
}
inline void push(int x){
heap[++size]=x;
reg int i=size,fa;
while(i>1){
fa=i>>1;
if(dis[heap[fa]]<=dis[heap[i]]) return;
std::swap(heap[fa],heap[i]);i=fa;
}
}
inline int pop(){
int ret=heap[1];heap[1]=heap[size--];
int i=1,ls,rs;
while((i<<1)<=size){
ls=i<<1;rs=ls|1;
if(rs<=size&&dis[heap[rs]]<dis[heap[ls]]) ls=rs;
if(dis[heap[ls]]>=dis[heap[i]]) break;
std::swap(heap[i],heap[ls]);i=ls;
}
return ret;
}
inline int dij(){
std::memset(dis,0x3f,sizeof dis);dis[start]=0;
push(start);in[start]=1;
while(size){
reg int u=pop(),v;in[u]=0;
for(reg int i=fir[u];i;i=nex[i]){
v=to[i];
if(dis[v]>dis[u]+w[i]){
dis[v]=dis[u]+w[i];
if(!in[v]) push(v),in[v]=1;
}
}
}
int ans=2e9;
for(reg int i=0;i<=k;i++) ans=std::min(ans,dis[i*n+end]);
return ans;
}
int main(){
n=read(),m=read(),k=read();
start=read()+1;end=read()+1;
for(reg int u,v,len,i=1;i<=m;i++){
u=read()+1;v=read()+1;len=read();
link(u,v,len);
for(reg int j=1;j<=k;j++) link(j*n+u,j*n+v,len),add((j-1)*n+u,j*n+v,0),add((j-1)*n+v,j*n+u,0);
}
std::printf("%d",dij());
return 0;
}