分层图最短路

分层图一般解决更改K个边权为0的问题。

例题,如果不知道分层图,这道题难以下手。但是如果知道,这道题就是大水题。

啥是分层图

对于路径上可以免费K条边的最短路。我们把这张图复制K张,新图有$K+1$个原图。然后,对于每两张相同的图之间,若两个点有边,则把上一张图的点连向下一条边,且边权为0。特别地,对于每张图的终点,要向下一张图的终点连边权为0的边(防止前面走的到但后面卡住了即用小于K次免费就可以到终点)。

大致如图。

分层图

看看代码实现吧!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
namespace Sonoda
{
template<typename T> void swap(T &a,T &b)
{
T t;
t=a;
a=b;
b=t;
}
template<typename T> T GCD(T a,T b)
{
if(b==0)
{
return a;
}
return GCD(b,a%b);
}
template<typename T>T Qpow(T a,T b,T p)
{
T res=1;
while(b)
{
if(b&1)
{
res*=a;
res%=p;
b--;
}
else
{
a*=a;
a%=p;
b>>=1;
}
}
return res;
}
template <typename T> void Ex_GCD(T a,T b,T &x,T &y)
{
if(b==0)
{
x=1;
y=0;
return;
}
Ex_GCD(b,a%b,x,y);
T t=x;
x=y;
y=t-a/b*y;
}
template<typename T> inline T read()
{
T num = 0, w = 1;
char c = 0;
while (c != '-' && !isdigit(c)) c = getchar();
if (c == '-') w = -1, c = getchar();
while (isdigit(c)) num = num * 10 + c - '0', c = getchar();
return num * w;
}
template<typename T> inline void write(T x)
{
if (x < 0) putchar('-'), x = -x;
if (x / 10) write(x / 10);
putchar(x % 10 + '0');
}
}
const int MAXN=5000050;
struct EDGE
{
int _next,to,w;
};
EDGE edge[MAXN];
int _count,head[MAXN];
int dis[MAXN],vis[MAXN];
struct NODE
{
int dis,pos;
};
NODE heap[MAXN];
int siz;
void Add_Line(const int &a,const int &b,const int &c)
{
_count++;
edge[_count]._next=head[a];
edge[_count].to=b;
edge[_count].w=c;
head[a]=_count;
}
void push(NODE x)
{
siz++;
heap[siz]=x;
int NowNode=siz;
while(NowNode>>1)
{
int NextNode=NowNode>>1;
if(heap[NextNode].dis>heap[NowNode].dis)
{
Sonoda::swap(heap[NextNode],heap[NowNode]);
NowNode=NextNode;
}
else
{
break;
}
}
}
void pop()
{
Sonoda::swap(heap[siz],heap[1]);
siz--;
int NowNode=1;
while((NowNode<<1)<=siz)
{
int NextNode=NowNode<<1;
if(NextNode+1<=siz&&heap[NextNode+1].dis<heap[NextNode].dis)
{
NextNode++;
}
if(heap[NextNode].dis<heap[NowNode].dis)
{
Sonoda::swap(heap[NowNode],heap[NextNode]);
NowNode=NextNode;
}
else
{
break;
}
}
}
NODE top()
{
return heap[1];
}
int main()
{
int N=Sonoda::read<int>();
int M=Sonoda::read<int>();
int K=Sonoda::read<int>();
int S=Sonoda::read<int>();
int T=Sonoda::read<int>();
S++;
T++;
for(int i=1;i<=M;i++)
{
int a,b,c;
a=Sonoda::read<int>();
b=Sonoda::read<int>();
c=Sonoda::read<int>();
a++;
b++;
Add_Line(a,b,c);
Add_Line(b,a,c);
for(int j=1;j<=K;j++)
{
Add_Line(a+(j*N),b+(j*N),c);
Add_Line(b+(j*N),a+(j*N),c);
Add_Line(a+(j-1)*N,b+(j)*N,0);
Add_Line(b+(j-1)*N,a+(j)*N,0);
}
}
for(int i=1;i<=K;i++)
{
Add_Line(T+(i-1)*N,T+(i)*N,0);
}
memset(dis,INF,sizeof(dis));
dis[S]=0;
push({0,S});
while(siz)
{
int NowNode=top().pos;
pop();
if(vis[NowNode])
{
continue;
}
vis[NowNode]=1;
for(int i=head[NowNode];i;i=edge[i]._next)
{
int NextNode=edge[i].to;
if(dis[NextNode]>dis[NowNode]+edge[i].w)
{
dis[NextNode]=dis[NowNode]+edge[i].w;
push({dis[NextNode],NextNode});
}
}
}
printf("%d",dis[N*(K)+T]);
return 0;
}

注意Add_Line的操作要加每个复制的图中的边,每两个相邻复制的图的免费边。

注意终点是最后一个图里的。