题目描述
题目链接
题目大意是给你一个n个点m条边的无向图,从一号点出发,走i条边,找出满足这个条件的路径的最长的长度,记为
l
i
l_i
li。然后求出
l
1
,
l
2
,
⋯
,
l
p
l_1,l_2,\cdots,l_p
l1,l2,⋯,lp之和,答案对
1
0
9
+
7
10^9 + 7
109+7取模。注意每条边都可以重复走。
数据范围
2
≤
n
≤
2000
2 \leq n\leq 2000
2≤n≤2000
n
−
1
≤
m
≤
2000
n - 1 \leq m \leq 2000
n−1≤m≤2000
m
≤
q
≤
1
0
9
m\leq q \leq 10^9
m≤q≤109
思路
通过简单的模拟可以看出,每一条最长路都应有两部分组成。第一部分是从1号点走过
k
k
k条边到达某条边
e
e
e的任意一个端点的最长路径,第二部分是在这条边
e
e
e上来回走
i
−
k
i-k
i−k次。
用数学语言来描述这段路径的长度
S
(
e
,
i
,
k
)
S_{(e, i, k)}
S(e,i,k):(设这条边的边权为
w
e
w_e
we它的两个端点为
u
u
u,
v
v
v,用d来表示到某个点的最长距离)
S
(
e
,
i
,
k
)
=
max
(
d
u
,
d
v
)
+
(
i
−
k
)
w
e
S_{(e,i, k)} = \max(d_u, d_v)+(i - k)w_e
S(e,i,k)=max(du,dv)+(i−k)we 当然
S
(
e
,
i
)
S_{(e,i)}
S(e,i)要在所有的
k
k
k中取一个最长的, 即:
S
(
e
,
i
)
=
max
1
≤
k
≤
n
−
1
S
(
e
,
i
,
k
)
S_{(e,i)} = \max_{1\leq k \leq n - 1} S_{(e,i, k)}
S(e,i)=1≤k≤n−1maxS(e,i,k)
至于为什么是
1
≤
k
≤
n
−
1
1\leq k \leq n - 1
1≤k≤n−1可以想象一下,从一号点出发走不超过
n
−
1
n-1
n−1条边就可以到达图中任何一个点,当然走的边不能重复,因为要保证尽可能多的在边e上重复走。
这样对于每一条重复走的边e我们都能求出来对应的
S
e
(
i
)
S_e(i)
Se(i)的函数表达式,并且可以发现S与i之间是线性关系。如图所示,不同的边对应的
S
e
(
i
)
S_e(i)
Se(i)会在不同的i值区间处于领先状态,最后构成图中红色的折线,把折线上的点的函数值累加起来就是答案了。
实现
求
d
d
d:
相当于求最长路用dp,注意这里要保证严格走i步,所以要加上一维,dp[i][j]表示走了i条边到达点j的最长路,当然可以用滚动数组优化掉i的那一维。这里给的代码用的遍历图的方法类似于Bellman-Ford,好处是不用邻接表存图。
求
S
e
(
i
)
S_e(i)
Se(i):
由于题目中i的数据范围是很大的,所以在时间和空间上都不允许将每一个
S
(
e
,
i
,
k
)
S_{(e,i,k)}
S(e,i,k)计算出来。我们可以把它换一个写法:
S
(
e
,
i
,
k
)
=
(
max
(
d
u
,
d
v
)
−
k
w
e
)
+
i
w
e
S_{(e, i,k)} =(\max(d_u, d_v) - kw_e) + iw_e
S(e,i,k)=(max(du,dv)−kwe)+iwe括号里面的这一项和i无关是可以被计算出来的,用
b
(
e
,
k
)
b_{(e,k)}
b(e,k)表示:
b
(
e
,
k
)
=
max
(
d
u
,
d
v
)
−
k
w
e
b_{(e,k)} = \max(d_u, d_v) - kw_e
b(e,k)=max(du,dv)−kwe
再令:
b
e
=
max
1
≤
k
≤
n
−
1
b
(
e
,
k
)
b_e = \max_{1\leq k \leq n - 1} b_{(e,k)}
be=1≤k≤n−1maxb(e,k)
注意到这里的
b
e
b_e
be就是
S
e
(
i
)
S_e(i)
Se(i)的截距,
w
e
w_e
we则是斜率:
S
e
(
i
)
=
w
e
i
+
b
e
S_e(i) = w_e i + b_e
Se(i)=wei+be
答案统计:
首先要确定每一个S_e是在哪一段上领先。方法是通过和其他边对应的
S
S
S求交点不断缩小上下界。例如对于两个边
i
,
j
i,j
i,j:
S
i
(
x
)
=
w
i
x
+
b
i
S_i(x) = w_ix+b_i
Si(x)=wix+bi
S
j
(
x
)
=
w
j
x
+
b
j
S_j(x) = w_jx + b_j
Sj(x)=wjx+bj解
S
i
(
x
)
>
S
j
(
x
)
S_i(x) > S_j(x)
Si(x)>Sj(x)
可得:当
w
i
>
w
j
w_i > w_j
wi>wj时得到一个下限
x
>
b
j
−
b
i
w
i
−
w
j
x>\frac{b_j - b_i}{w_i - w_j}
x>wi−wjbj−bi当
w
i
<
w
j
w_i < w_j
wi<wj 时得到一个上限
x
<
b
j
−
b
i
w
i
−
w
j
x<\frac{b_j - b_i}{w_i - w_j}
x<wi−wjbj−bi
相等的时候需要特殊考虑。
得到区间l到r后,用等差数列求和公式计算答案贡献:
∑
i
=
l
r
(
w
e
i
+
b
e
)
=
(
l
+
r
)
(
r
−
l
+
1
)
2
w
e
+
(
r
−
l
+
1
)
b
e
\sum_{i=l}^{r}(w_ei+b_e) = \frac{(l + r)(r - l + 1)}{2}w_e + (r - l + 1)b_e
i=l∑r(wei+be)=2(l+r)(r−l+1)we+(r−l+1)be
具体细节参照代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e3 + 5;
const int MAXM = 2e3 + 5;
const int INF = 1e9;
const int Mod = 1e9 + 7;
int u[MAXM], v[MAXM], w[MAXM], n, m, q;
long long dp[2][MAXN], b[MAXN], ans;
signed main() {
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= m; i++) {
scanf("%d%d%d", u + i, v + i, w + i);
}
for (int i = 1; i <= n; i++) dp[0][i] = dp[1][i] = -INF;
for (int i = 1; i <= m; i++) b[i] = -INF;
dp[0][1] = 0;
//这里枚举的i其实就是上文提到的k
for (int i = 1; i < n; i++) {
for (int j = 1; j <= m; j++) {
dp[i & 1][v[j]] = max(dp[i & 1][v[j]], dp[i - 1 & 1][u[j]] + w[j]);
dp[i & 1][u[j]] = max(dp[i & 1][u[j]], dp[i - 1 & 1][v[j]] + w[j]);
}
for (int j = 1; j <= m; j++)
b[j] = max(b[j], max(dp[i & 1][u[j]], dp[i & 1][v[j]]) - i * w[j]);
ans = (ans + *max_element(dp[i & 1] + 1, dp[i & 1] + n + 1)) % Mod;
}
for (int i = 1; i <= m; i++) {
long long l = n, r = q;
for (int j = 1; l <= r && j <= m; j++) if (i != j) {
long long k = w[i] - w[j];
long long db = b[j] - b[i];
if (k > 0)
//加一是为了防止重复计算
l = max(l, db / k + 1);
if (k < 0)
r = min(r, db / k);
if (k == 0) {
//如果wi = wj并且bj < bi 时Si(x)的图像在Sj(x)图像的上面,上下限不做变动
//如果wi = wj并且bj = bi 说明这两个图像一样,如果j>i说明这部分答案还没算在内,需要算入答案。
//如果上面两种情况都不满足,说明这种情况不是答案的一部分直接退出。
if (!(db < 0 || (db == 0 && j > i)))
r = -1;
}
}
if (l <= r)
//统计区间和需要等差数列求和公式
ans = (ans + (l + r) * (r - l + 1) / 2 % Mod * w[i] % Mod + (b[i] % Mod + Mod) % Mod * (r - l + 1) % Mod) % Mod;
}
printf("%lld\n", ans);
return 0;
}