analysis
基本上是第一次见这个模式(上一次是在luogu P3119)
这个模式的特征大概就是:在一个正常的图上可以进行 k 次决策,对于每次决策,不影响图的结构,只影响目前的状态或代价
那么这个题就是这个模式嘛,可以做k次决策,做了之后会影响到后面走的距离,图实际上没有改变
于是就可以拆点分层了,把一个点强行拽成k个,然后就可以连边了。怎么连呢?首先原来就有的边是不能不连的,而且还要在每一层图上都连。接下来就要确定每层图之间的关系了,从第i层到第i+1层的边边权全为0,等于说是用掉了一次免费卡,于是愉快的连一连,最后跑一边最短路就可以了(最好用dij,spfa已经被卡的不要不要的了,如luogu P2939)
code
#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
#define loop(i,start,end) for(register int i=start;i<=end;++i)
#define anti_loop(i,start,end) for(register int i=start;i>=end;--i)
#define clean(arry,num) memset(arry,num,sizeof(arry))
#define isdegit(a) ((a>='0'&&a<='9'))
#define ll long long
template<typename T>void read(T &x){
x=0;char r=getchar();T neg=1;
while(!isdegit(r)){if(r=='-')neg=-1;r=getchar();}
while(isdegit(r)){x=(x<<1)+(x<<3)+r-'0';r=getchar();}
x*=neg;
}
int n,m,k;
int S,T;
const int maxn=(5e4+10)*50;
struct node{
int e;
int w;
int nxt;
}edge[maxn];
int head[maxn];
int cnt=0;
inline void addl(register int u,register int v,register int w){
edge[++cnt].e=v;
edge[cnt].w=w;
edge[cnt].nxt=head[u];
head[u]=cnt;
}
int dis[maxn];
deque<int>q;
void spfa(){
clean(dis,0x3f);
dis[S]=0;
q.push_front(S);
while(!q.empty()){
int f=q.front();
q.pop_front();
for(int i=head[f];i!=-1;i=edge[i].nxt){
int v=edge[i].e;
if(dis[v]>dis[f]+edge[i].w){
dis[v]=dis[f]+edge[i].w;
if(!q.empty()&&dis[v]<dis[q.front()])
q.push_front(v);
else q.push_back(v);
}
}
}
int minn=INT_MAX;
loop(i,0,k)
minn=min(minn,dis[T+n*i]);
printf("%d\n",minn);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("datain.txt","r",stdin);
#endif
clean(head,-1);
read(n);
read(m);
read(k);
read(S);
read(T);
++S,++T;
register int ui,vi,ti;
loop(i,1,m){
read(ui);
read(vi);
++ui;
++vi;
read(ti);
loop(_k,0,k){
addl(ui+_k*n,vi+_k*n,ti);
addl(vi+_k*n,ui+_k*n,ti);
if(_k){
addl(ui+(_k-1)*n,vi+_k*n,0);
addl(vi+(_k-1)*n,ui+_k*n,0);
}
}
}
spfa();
return 0;
}
双倍经验: