CSP-J 2023年题解
小苹果
题目描述
小 Y 的桌子上放着 n n n 个苹果从左到右排成一列,编号为从 1 1 1 到 n n n。
小苞是小 Y 的好朋友,每天她都会从中拿走一些苹果。
每天在拿的时候,小苞都是从左侧第 1 1 1 个苹果开始、每隔 2 2 2 个苹果拿走 1 1 1 个苹果。随后小苞会将剩下的苹果按原先的顺序重新排成一列。
小苞想知道,多少天能拿完所有的苹果,而编号为 n n n 的苹果是在第几天被拿走的?
输入格式
输入的第一行包含一个正整数 n n n,表示苹果的总数。
输出格式
输出一行包含两个正整数,两个整数之间由一个空格隔开,分别表示小苞拿走所有苹果所需的天数以及拿走编号为 n n n 的苹果是在第几天。
输入输出样例 #1
输入 #1
8
输出 #1
5 5
说明/提示
【样例 1 1 1 解释】
小苞的桌上一共放了 8 8 8 个苹果。
小苞第一天拿走了编号为 1 1 1、 4 4 4、 7 7 7 的苹果。
小苞第二天拿走了编号为 2 2 2、 6 6 6 的苹果。
小苞第三天拿走了编号为 3 3 3 的苹果。
小苞第四天拿走了编号为 5 5 5 的苹果。
小苞第五天拿走了编号为 8 8 8 的苹果。
【样例 2 2 2】
见选手目录下的 apple/apple2.in 与 apple/apple2.ans。
【数据范围】
对于所有测试数据有: 1 ≤ n ≤ 1 0 9 1\leq n\leq 10^9 1≤n≤109。
测试点 | n ≤ n\leq n≤ | 特殊性质 |
---|---|---|
1 ∼ 2 1\sim 2 1∼2 | 10 10 10 | 无 |
3 ∼ 5 3\sim 5 3∼5 | 1 0 3 10^3 103 | 无 |
6 ∼ 7 6\sim 7 6∼7 | 1 0 6 10^6 106 | 有 |
8 ∼ 9 8\sim 9 8∼9 | 1 0 6 10^6 106 | 无 |
10 10 10 | 1 0 9 10^9 109 | 无 |
特殊性质:小苞第一天就取走编号为 n n n 的苹果。
解题思路
每隔两个拿走一个,换一种说法,就是当 i n d e x m o d 3 = 1 index \mod 3 = 1 indexmod3=1 的时候,第 i n d e x index index 个苹果会被拿走。
所以如果某一天只剩下了 m m m 个苹果,并且 m m o d 3 = 1 m \mod 3 = 1 mmod3=1 的时候,第 n n n 个苹果就被拿走了。至于计算每天被拿走的苹果数量,我们用这个公式来计算。
n u m = ⌈ m / 3 ⌉ num = \lceil m / 3 \rceil num=⌈m/3⌉
cpp
#include <bits/stdc++.h>
using namespace std;
int n, ans1, ans2;
int main()
{
cin >> n;
while (n)
{
ans1++;
if (n % 3 == 1 && ans2 == 0) // 看看啥时候取最后一个
ans2 = ans1;
n -= n / 3 + (int)(n % 3 != 0); // 记得向上取整
}
cout << ans1 << " " << ans2 << '\n';
return 0;
}
因为每次大概要少 1 3 \frac{1}{3} 31 的苹果,所以时间复杂度就是 l o g log log 级别。
公路
题目描述
小苞准备开着车沿着公路自驾。
公路上一共有 n n n 个站点,编号为从 1 1 1 到 n n n。其中站点 i i i 与站点 i + 1 i + 1 i+1 的距离为 v i v_i vi 公里。
公路上每个站点都可以加油,编号为 i i i 的站点一升油的价格为 a i a_i ai 元,且每个站点只出售整数升的油。
小苞想从站点 1 1 1 开车到站点 n n n,一开始小苞在站点 1 1 1 且车的油箱是空的。已知车的油箱足够大,可以装下任意多的油,且每升油可以让车前进 d d d 公里。问小苞从站点 1 1 1 开到站点 n n n,至少要花多少钱加油?
输入格式
输入的第一行包含两个正整数 n n n 和 d d d,分别表示公路上站点的数量和车每升油可以前进的距离。
输入的第二行包含 n − 1 n - 1 n−1 个正整数 v 1 , v 2 ... v n − 1 v_1, v_2\dots v_{n-1} v1,v2...vn−1,分别表示站点间的距离。
输入的第三行包含 n n n 个正整数 a 1 , a 2 ... a n a_1, a_2 \dots a_n a1,a2...an,分别表示在不同站点加油的价格。
输出格式
输出一行,仅包含一个正整数,表示从站点 1 1 1 开到站点 n n n,小苞至少要花多少钱加油。
输入输出样例 #1
输入 #1
5 4
10 10 10 10
9 8 9 6 5
输出 #1
79
说明/提示
【样例 1 解释】
最优方案下:小苞在站点 1 1 1 买了 3 3 3 升油,在站点 2 2 2 购买了 5 5 5 升油,在站点 4 4 4 购买了 2 2 2 升油。
【样例 2】
见选手目录下的 road/road2.in 与 road/road2.ans。
【数据范围】
对于所有测试数据保证: 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105, 1 ≤ d ≤ 1 0 5 1 \leq d \leq 10^5 1≤d≤105, 1 ≤ v i ≤ 1 0 5 1 \leq v_i \leq 10^5 1≤vi≤105, 1 ≤ a i ≤ 1 0 5 1 \leq a_i \leq 10^5 1≤ai≤105。
测试点 | n ≤ n \leq n≤ | 特殊性质 |
---|---|---|
1 ∼ 5 1\sim 5 1∼5 | 8 8 8 | 无 |
6 ∼ 10 6\sim 10 6∼10 | 1 0 3 10^3 103 | 无 |
11 ∼ 13 11\sim 13 11∼13 | 1 0 5 10^5 105 | A |
14 ∼ 16 14\sim 16 14∼16 | 1 0 5 10^5 105 | B |
17 ∼ 20 17\sim 20 17∼20 | 1 0 5 10^5 105 | 无 |
- 特殊性质 A:站点 1 1 1 的油价最低。
- 特殊性质 B:对于所有 1 ≤ i < n 1 \leq i < n 1≤i<n, v i v_i vi 为 d d d 的倍数。
解题思路
简单贪心题,我们从站点一运动到站点 n n n,车可以装任意多的油并且加油站里面的油也足够多,问最少花多少钱。
我们很简单就能想到,每次我们加油,只需要保证可以到达距离当前加油站最近,并且比当前加油站更便宜的其他加油站即可。
因为我们在当前加油站加油不如走到更便宜的加油站之后再加。
本题代码要比思路难搞。
cpp
#include <bits/stdc++.h>
#define int long long // 这里偷懒了,大家不要向我学习
using namespace std;
const int maxn = 1e5 + 5;
int n, d;
int v[maxn], a[maxn];
signed main()
{
cin >> n >> d;
for (int i = 2; i <= n; i++)
cin >> v[i], v[i] += v[i - 1]; // 把 v 变成从起点走到某一个点的距离
for (int i = 1; i <= n; i++)
cin >> a[i];
int ans = 0, res = 0; // 因为只能买整数单位油,所以有可能走到第 i 个加油站之后还能再往前走一走,这里的 res 记录的是多往前走的距离
int pre = 1;
for (int i = 2; i <= n; i++)
{
if (a[i] < a[pre]) // 发现了比上一个加油站更便宜的
{
int gas = (v[i] - v[pre] - res + d - 1) / d; // 走到当前加油站需要多少油,注意上取整
ans += gas * a[pre]; // 买这些油需要多少钱
res = gas * d + res - (v[i] - v[pre]);
pre = i;
}
}
if (pre != n) // 最后在第 pre 个加油站加油直接走到 n
{
int gas = (v[n] - v[pre] - res + d - 1) / d;
ans += gas * a[pre];
res = gas * d + res - (v[n] - v[pre]);
}
cout << ans << '\n';
return 0;
}
一元二次方程
题目背景
众所周知,对一元二次方程 a x 2 + b x + c = 0 , ( a ≠ 0 ) ax ^ 2 + bx + c = 0, (a \neq 0) ax2+bx+c=0,(a=0),可以用以下方式求实数解:
- 计算 Δ = b 2 − 4 a c \Delta = b ^ 2 - 4ac Δ=b2−4ac,则:
- 若 Δ < 0 \Delta < 0 Δ<0,则该一元二次方程无实数解。
- 否则 Δ ≥ 0 \Delta \geq 0 Δ≥0,此时该一元二次方程有两个实数解 x 1 , 2 = − b ± Δ 2 a x _ {1, 2} = \frac{-b \pm \sqrt \Delta}{2a} x1,2=2a−b±Δ 。
例如:
- x 2 + x + 1 = 0 x ^ 2 + x + 1 = 0 x2+x+1=0 无实数解,因为 Δ = 1 2 − 4 × 1 × 1 = − 3 < 0 \Delta = 1 ^ 2 - 4 \times 1 \times 1 = -3 < 0 Δ=12−4×1×1=−3<0。
- x 2 − 2 x + 1 = 0 x ^ 2 - 2x + 1 = 0 x2−2x+1=0 有两相等实数解 x 1 , 2 = 1 x _ {1, 2} = 1 x1,2=1。
- x 2 − 3 x + 2 = 0 x ^ 2 - 3x + 2 = 0 x2−3x+2=0 有两互异实数解 x 1 = 1 , x 2 = 2 x _ 1 = 1, x _ 2 = 2 x1=1,x2=2。
在题面描述中 a a a 和 b b b 的最大公因数使用 gcd ( a , b ) \gcd(a, b) gcd(a,b) 表示。例如 12 12 12 和 18 18 18 的最大公因数是 6 6 6,即 gcd ( 12 , 18 ) = 6 \gcd(12, 18) = 6 gcd(12,18)=6。
题目描述
现在给定一个一元二次方程的系数 a , b , c a, b, c a,b,c,其中 a , b , c a, b, c a,b,c 均为整数且 a ≠ 0 a \neq 0 a=0 。你需要判断一元二次方程 a x 2 + b x + c = 0 a x ^ 2 + bx + c = 0 ax2+bx+c=0 是否有实数解,并按要求的格式输出。
在本题中输出有理数 v v v 时须遵循以下规则:
-
由有理数的定义,存在唯一的两个整数 p p p 和 q q q,满足 q > 0 q > 0 q>0, gcd ( p , q ) = 1 \gcd(p, q) = 1 gcd(p,q)=1 且 v = p q v = \frac pq v=qp。
-
若 q = 1 q = 1 q=1,则输出
{p}
,否则输出{p}/{q}
,其中{n}
代表整数 n n n 的值; -
例如:
- 当 v = − 0.5 v = -0.5 v=−0.5 时, p p p 和 q q q 的值分别为 − 1 -1 −1 和 2 2 2,则应输出
-1/2
; - 当 v = 0 v = 0 v=0 时, p p p 和 q q q 的值分别为 0 0 0 和 1 1 1,则应输出
0
。
- 当 v = − 0.5 v = -0.5 v=−0.5 时, p p p 和 q q q 的值分别为 − 1 -1 −1 和 2 2 2,则应输出
对于方程的求解,分两种情况讨论:
-
若 Δ = b 2 − 4 a c < 0 \Delta = b ^ 2 - 4ac < 0 Δ=b2−4ac<0,则表明方程无实数解,此时你应当输出
NO
; -
否则 Δ ≥ 0 \Delta \geq 0 Δ≥0,此时方程有两解(可能相等),记其中较大者为 x x x,则:
-
若 x x x 为有理数,则按有理数的格式输出 x x x。
-
否则根据上文公式, x x x 可以被唯一 表示为 x = q 1 + q 2 r x = q _ 1 + q _ 2 \sqrt r x=q1+q2r 的形式,其中:
-
q 1 , q 2 q _ 1, q _ 2 q1,q2 为有理数,且 q 2 > 0 q _ 2 > 0 q2>0;
-
r r r 为正整数且 r > 1 r > 1 r>1,且不存在正整数 d > 1 d > 1 d>1 使 d 2 ∣ r d ^ 2 \mid r d2∣r(即 r r r 不应是 d 2 d ^ 2 d2 的倍数);
-
此时:
- 若 q 1 ≠ 0 q _ 1 \neq 0 q1=0,则按有理数的格式输出 q 1 q _ 1 q1,并再输出一个加号
+
; - 否则跳过这一步输出;
随后:
- 若 q 2 = 1 q _ 2 = 1 q2=1,则输出
sqrt({r})
; - 否则若 q 2 q _ 2 q2 为整数,则输出
{q2}*sqrt({r})
; - 否则若 q 3 = 1 q 2 q _ 3 = \frac 1{q _ 2} q3=q21 为整数,则输出
sqrt({r})/{q3}
; - 否则可以证明存在唯一整数 c , d c, d c,d 满足 c , d > 1 , gcd ( c , d ) = 1 c, d > 1, \gcd(c, d) = 1 c,d>1,gcd(c,d)=1 且 q 2 = c d q _ 2 = \frac cd q2=dc,此时输出
{c}*sqrt({r})/{d}
;
上述表示中
{n}
代表整数{n}
的值,详见样例。如果方程有实数解,则按要求的格式输出两个实数解中的较大者。否则若方程没有实数解,则输出
NO
。 -
输入格式
输入的第一行包含两个正整数 T , M T, M T,M,分别表示方程数和系数的绝对值上限。
接下来 T T T 行,每行包含三个整数 a , b , c a, b, c a,b,c。
输出格式
输出 T T T 行,每行包含一个字符串,表示对应询问的答案,格式如题面所述。
每行输出的字符串中间不应包含任何空格。
输入输出样例 #1
输入 #1
9 1000
1 -1 0
-1 -1 -1
1 -2 1
1 5 4
4 4 1
1 0 -432
1 -3 1
2 -4 1
1 7 1
输出 #1
1
NO
1
-1
-1/2
12*sqrt(3)
3/2+sqrt(5)/2
1+sqrt(2)/2
-7/2+3*sqrt(5)/2
说明/提示
【样例 #2】
见附件中的 uqe/uqe2.in
与 uqe/uqe2.ans
。
【数据范围】
对于所有数据有: 1 ≤ T ≤ 5000 1 \leq T \leq 5000 1≤T≤5000, 1 ≤ M ≤ 1 0 3 1 \leq M \leq 10 ^ 3 1≤M≤103, ∣ a ∣ , ∣ b ∣ , ∣ c ∣ ≤ M |a|,|b|,|c| \leq M ∣a∣,∣b∣,∣c∣≤M, a ≠ 0 a \neq 0 a=0。
测试点编号 | M ≤ M \leq M≤ | 特殊性质 A | 特殊性质 B | 特殊性质 C |
---|---|---|---|---|
1 1 1 | 1 1 1 | 是 | 是 | 是 |
2 2 2 | 20 20 20 | 否 | 否 | 否 |
3 3 3 | 1 0 3 10 ^ 3 103 | 是 | 否 | 是 |
4 4 4 | 1 0 3 10 ^ 3 103 | 是 | 否 | 否 |
5 5 5 | 1 0 3 10 ^ 3 103 | 否 | 是 | 是 |
6 6 6 | 1 0 3 10 ^ 3 103 | 否 | 是 | 否 |
7 , 8 7, 8 7,8 | 1 0 3 10 ^ 3 103 | 否 | 否 | 是 |
9 , 10 9, 10 9,10 | 1 0 3 10 ^ 3 103 | 否 | 否 | 否 |
其中:
- 特殊性质 A:保证 b = 0 b = 0 b=0;
- 特殊性质 B:保证 c = 0 c = 0 c=0;
- 特殊性质 C:如果方程有解,那么方程的两个解都是整数。
解题思路
纯纯的模拟题,做这种题的时候一定要理清思路,模块化你的代码,方便调试。
具体思路看下面代码:
cpp
#include <bits/stdc++.h>
#define int long long // 偷懒了
using namespace std;
int T, m;
int a, b, c;
int F; // F 判断左边输出了没
int delta(int a, int b, int c) // 计算 Δ
{
return b * b - 4 * a * c;
}
int gcd(int x, int y) // 两个数字的最大公约数
{
if (y == 0)
return x;
return gcd(y, x % y);
}
void left() // 计算 -b/2a
{
int d = delta(a, b, c), p, q;
p = -b;
q = 2 * a;
if ((int)sqrt(d) * (int)sqrt(d) == d) // 如果 Δ 可以被开方就直接加到 -b 里面
if (a > 0)
p += sqrt(d);
else
p -= sqrt(d);
int f = 1; // 通过除号上下两部分判断整体的正负
if (p < 0) f = -f;
if (q < 0) f = -f;
p = abs(p); // 取绝对值,方便输出
q = abs(q);
if (p == 0) // 左边是 0 不用输出
{
F = 1; // 标记一下
return ;
}
if (p % q == 0) //可以被整除
cout << f * p / q;
else
{
int g = gcd(p, q); // 化简后输出
cout << f * p / g << "/" << q / g;
}
}
void right() // +/-sqrt(Δ)/2a
{
int d = delta(a, b, c), f = 1; // 用 f 判断正负
if ((int)sqrt(d) * (int)sqrt(d) == d) // 如果可以被开方,说明 left 函数已经计算过一次了,直接结束
{
if (F == 1) // 左边没输出,说明结果是 0
cout << 0;
return ;
}
if (a < 0)
f = -1;
int k = 1, q = abs(2 * a);
for (int i = 2; i <= sqrt(d); i++) // 尝试对 Δ 进行开方,把整数提出来
while (d % (i * i) == 0)
{
k *= i;
d /= i * i;
}
// 我们要注意一点,题目让我们输出较大值,那么当 2a 是负数,sqrt(Δ) 前面也是负号,当 2a 是正数,sqrt(Δ) 前面就是 + 号,所以无论如何都该输出 "+"
if (F == 0) // 左边如果输出了,那么我们用符号进行连接
cout << "+";
int g = gcd(k, q); // 求最大公约数方便化简
if (k != g)
cout << k / g << "*"; // 整除
cout << "sqrt(" << d << ")";
if (q != g) // 看看 2a 是不是需要化简
cout << "/" << q / g;
return ;
}
signed main()
{
cin >> T >> m;
while (T--) // 多组数据
{
cin >> a >> b >> c;
int d = delta(a, b, c);
if (d < 0)
{
cout << "NO" << '\n';
continue;
}
left();
right();
F = 0;
cout << '\n';
}
return 0;
}
旅游巴士
题目描述
小 Z 打算在国庆假期期间搭乘旅游巴士去一处他向往已久的景点旅游。
旅游景点的地图共有 n n n 处地点,在这些地点之间连有 m m m 条道路。其中 1 1 1 号地点为景区入口, n n n 号地点为景区出口。我们把一天当中景区开门营业的时间记为 0 0 0 时刻,则从 0 0 0 时刻起,每间隔 k k k 单位时间便有一辆旅游巴士到达景区入口,同时有一辆旅游巴士从景区出口驶离景区。
所有道路均只能单向通行 。对于每条道路,游客步行通过的用时均为恰好 1 1 1 单位时间。
小 Z 希望乘坐旅游巴士到达景区入口,并沿着自己选择的任意路径走到景区出口,再乘坐旅游巴士离开,这意味着他到达和离开景区的时间都必须是 k k k 的非负整数倍 。由于节假日客流众多,小 Z 在旅游巴士离开景区前只想一直沿着景区道路移动,而不想在任何地点(包括景区入口和出口)或者道路上停留。
出发前,小 Z 忽然得知:景区采取了限制客流的方法,对于每条道路均设置了一个
"开放时间" a i a _ i ai,游客只有不早于 a i a _ i ai 时刻才能通过这条道路。
请帮助小 Z 设计一个旅游方案,使得他乘坐旅游巴士离开景区的时间尽量地早。
输入格式
输入的第一行包含 3 个正整数 n , m , k n, m, k n,m,k,表示旅游景点的地点数、道路数,以及旅游巴士的发车间隔。
输入的接下来 m m m 行,每行包含 3 个非负整数 u i , v i , a i u _ i, v _ i, a_ i ui,vi,ai,表示第 i i i 条道路从地点 u i u _ i ui 出发,到达地点 v i v _ i vi,道路的"开放时间"为 a i a _ i ai。
输出格式
输出一行,仅包含一个整数,表示小 Z 最早乘坐旅游巴士离开景区的时刻。如果不存在符合要求的旅游方案,输出 -1
。
输入输出样例 #1
输入 #1
5 5 3
1 2 0
2 5 1
1 3 0
3 4 3
4 5 1
输出 #1
6
说明/提示
【样例 #1 解释】
小 Z 可以在 3 3 3 时刻到达景区入口,沿 1 → 3 → 4 → 5 1 \to 3 \to 4 \to 5 1→3→4→5 的顺序走到景区出口,并在 6 6 6 时刻离开。
【样例 #2】
见附件中的 bus/bus2.in
与 bus/bus2.ans
。
【数据范围】
对于所有测试数据有: 2 ≤ n ≤ 1 0 4 2 \leq n \leq 10 ^ 4 2≤n≤104, 1 ≤ m ≤ 2 × 1 0 4 1 \leq m \leq 2 \times 10 ^ 4 1≤m≤2×104, 1 ≤ k ≤ 100 1 \leq k \leq 100 1≤k≤100, 1 ≤ u i , v i ≤ n 1 \leq u _ i, v _ i \leq n 1≤ui,vi≤n, 0 ≤ a i ≤ 1 0 6 0 \leq a _ i \leq 10 ^ 6 0≤ai≤106。
测试点编号 | n ≤ n \leq n≤ | m ≤ m \leq m≤ | k ≤ k \leq k≤ | 特殊性质 |
---|---|---|---|---|
1 ∼ 2 1 \sim 2 1∼2 | 10 10 10 | 15 15 15 | 100 100 100 | a i = 0 a _ i = 0 ai=0 |
3 ∼ 5 3 \sim 5 3∼5 | 10 10 10 | 15 15 15 | 100 100 100 | 无 |
6 ∼ 7 6 \sim 7 6∼7 | 1 0 4 10 ^ 4 104 | 2 × 1 0 4 2 \times 10 ^ 4 2×104 | 1 1 1 | a i = 0 a _ i = 0 ai=0 |
8 ∼ 10 8 \sim 10 8∼10 | 1 0 4 10 ^ 4 104 | 2 × 1 0 4 2 \times 10 ^ 4 2×104 | 1 1 1 | 无 |
11 ∼ 13 11 \sim 13 11∼13 | 1 0 4 10 ^ 4 104 | 2 × 1 0 4 2 \times 10 ^ 4 2×104 | 100 100 100 | a i = 0 a _ i = 0 ai=0 |
14 ∼ 15 14 \sim 15 14∼15 | 1 0 4 10 ^ 4 104 | 2 × 1 0 4 2 \times 10 ^ 4 2×104 | 100 100 100 | u i ≤ v i u _ i \leq v _ i ui≤vi |
16 ∼ 20 16 \sim 20 16∼20 | 1 0 4 10 ^ 4 104 | 2 × 1 0 4 2 \times 10 ^ 4 2×104 | 100 100 100 | 无 |
解题思路
动态规划 + 最短路,在我们建完这个有向图之后,我们需要考虑一下状态转移的问题。
当我们从一个点转移到另外一个点,然后发现因为开放时间的问题,我们没有办法进行移动的时候。我们可以将时间加上若干个 k,也就相当于我晚几个时间 k 进入景点,这样的话就合法了。
我们令 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示从起点出发到达 i i i 号点,花费时间 % k \%k %k 等于 j j j 所需要花费的最小时间,那么我们要求的最终答案就是 d p [ n ] [ 0 ] dp[n][0] dp[n][0]。
我们可以用类似于 dijkstra 的操作(spfa应该也行)来进行状态转移。
cpp
#include <bits/stdc++.h>
#define int long long // 偷懒了
#define INF 1000000000000000000 // 极大值
using namespace std;
const int maxn = 1e4 + 5;
int n, m, k, dis[maxn][105], head[maxn], tot;
bool vis[maxn][105];
struct edge{
int nxt, to, w;
}e[maxn << 1];
void addedge(int u, int v, int w) // 链式前向星建边
{
e[++tot].nxt = head[u];
e[tot].to = v;
e[tot].w = w;
head[u] = tot;
}
int Ceil(int a, int b) // 把 a 加到恰好比 b 大
{
return a + (b - a + k - 1) / k * k;
}
void dijkstra()
{
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
for (int i = 1; i <= n; i++) // 初始化成极大值
for (int j = 0; j <= 100; j++)
dis[i][j] = INF;
dis[1][0] = 0; // 起点达到的时间肯定是 0
q.push(make_pair(0, 1));
while (!q.empty())
{
int u = q.top().second, p = q.top().first;
q.pop();
if (vis[u][p % k] == 1)
continue;
vis[u][p % k] = 1;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to, w = e[i].w;
if (p >= w) // 这条边可以过
{
if (dis[v][(p + 1) % k] > p + 1) // 当前这个走法可以更新状态
{
dis[v][(p + 1) % k] = p + 1; // 更新一下
q.push(make_pair(p + 1, v));
}
}
else // 过不了
{
int ti = Ceil(p, w); // 加若干个 k 使得自己可以通过
if (dis[v][(p + 1) % k] > ti + 1)
{
dis[v][(p + 1) % k] = ti + 1; // 更新
q.push(make_pair(ti + 1, v));
}
}
}
}
}
signed main()
{
cin >> n >> m >> k;
for (int i = 1; i <= m; i++) // 建边
{
int u, v, w;
cin >> u >> v >> w;
addedge(u, v, w);
}
dijkstra();
if (dis[n][0] == INF) // 到达不了
cout << -1;
else
cout << dis[n][0]; // 否则输出答案
return 0;
}