思路
文章题意很复杂,大概归纳一下:
每一个从
A
i
A_i
Ai 到
B
i
B_i
Bi 的火车的发车时刻都是等差数列:
l
i
,
l
i
+
d
i
,
l
i
+
2
d
i
⋯
l
i
+
(
k
i
−
1
)
d
i
l_i,l_i+d_i,l_i+2d_i\cdots l_i+(k_i-1)d_i
li,li+di,li+2di⋯li+(ki−1)di
每辆车都要花
c
i
c_i
ci 的时间到达
B
i
B_i
Bi
这很明显就是一个有向图,也没有负环,合适用Dijkstra。
我们把每条边的方向反一反。
不过,每条边的边权并不是一个固定的数值。
假设如今正在遍历第
i
i
i 条边,此时的
f
(
B
i
)
f(B_i)
f(Bi) 是已知的,要通过
f
(
B
i
)
f(B_i)
f(Bi) 来松弛
f
(
A
i
)
f(A_i)
f(Ai) 。
例如样例1中的 3 3 3 号车站,发现经过它的列车时刻表为:
这时,表格右侧标橙的三辆列车正好衔接上了。
在第一次列车从
l
i
l_i
li 发车后,要不晚于
f
(
B
i
)
f(B_i)
f(Bi) 时刻到达
B
i
B_i
Bi ,意味着不晚于发车后
f
(
B
i
)
−
l
i
−
c
i
f(B_i) -l_i-c_i
f(Bi)−li−ci 时刻乘上火车,乘坐第
⌊
f
(
B
i
)
−
l
i
−
c
i
d
i
⌋
\lfloor \frac{f(B_i) -l_i-c_i}{d_i}\rfloor
⌊dif(Bi)−li−ci⌋ (编号从
0
0
0 开始) 辆火车出发。
注意:当
f
(
B
i
)
−
l
i
−
c
i
<
0
f(B_i) -l_i-c_i<0
f(Bi)−li−ci<0 时,说明此时的最晚出发时间已经早于首班车,则乘不上车。
所以有以下递推式:
f
(
A
i
)
=
⌊
f
(
B
i
)
−
l
i
−
c
i
d
i
⌋
d
i
+
l
i
f(A_i)=\lfloor \frac{f(B_i) -l_i-c_i}{d_i}\rfloor d_i+l_i
f(Ai)=⌊dif(Bi)−li−ci⌋di+li
易错点
- ⌊ f ( B i ) − l i − c i d i ⌋ \lfloor \frac{f(B_i) -l_i-c_i}{d_i}\rfloor ⌊dif(Bi)−li−ci⌋ 并不等于c++中的
(arri[now] - g[now][i].l - g[now][i].c) / g[now][i].d;
除法并不完全是下取整
2.最短路中,除了节点编号,其它变量要记得开long long。
代码
void dijkstra(){
priority_queue<pair<long long,int> > pq;
pq.push(make_pair(inf,n));
while(!pq.empty()){
int now = pq.top().second;
long long v = pq.top().first;
pq.pop();
if(vis[now] == true) continue;
arri[now] = v;
vis[now] = true;
for(int i = 0;i < g[now].size();i ++){
if(vis[g[now][i].b] == false){
long long num;
if(v == inf) num = g[now][i].k - 1;
else{
if(arri[now] - g[now][i].l - g[now][i].c < 0) continue;
num = (arri[now] - g[now][i].l - g[now][i].c) / g[now][i].d;
num = mmin(num,g[now][i].k - 1);
}
long long cal = g[now][i].l + 1LL * num * g[now][i].d;
if(cal > arri[g[now][i].b]){
arri[g[now][i].b] = cal;
pq.push(make_pair(cal,g[now][i].b));
}
}
}
}
}