Problem D
很巧妙的思维题
给你一张
n
(
n
≤
500
)
n(n\leq 500)
n(n≤500) 个点,
m
(
m
≤
2.5
e
5
)
m(m\leq2.5e5)
m(m≤2.5e5) 条带权无向边的图。你的目标是从 1 走到 n。
你可以进行如下操作任意次:
问:从 1 到 n n n 的最短时间,时间 = 修改边花费的时间 + 最终从 1 走到 n n n 的路径长度。
假设我们现在已经通过进行若干次操作,找到了最优解对应的最后的图。那么这个路径一定满足以下条件:
证明:对于1,假设某个点连接的两条边权值不同,我们可以通过一次操作将其合并成一条边,权值为
m
i
n
(
w
1
,
w
2
)
×
2
min(w_1,w_2)\times 2
min(w1,w2)×2 ,因此一定相同。条件2也类似,把
k
k
k 条权值为
w
w
w 的边合并成
1
1
1 条边与不合并花费相同。
于是猜测最终答案可能和路径上边的数量以及路径上最短路长度有关。(因为长的边可以被短的替代)
首先我们用 floyd 算法求出任意两点间最少经过多少条边,然后可以枚举每条边是否是最终的
w
w
w 。
考虑如何从
1
→
n
1\to n
1→n ,有两种方式
int n, m;
int f[N][N];
int u[maxm], v[maxm], w[maxm];
void solve() {
cin >> n >> m;
for(int i = 1; i <= m; i++) {
cin >> u[i] >> v[i] >> w[i];
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
f[i][j] = n + 1;
}
f[i][i] = 0;
}
for(int i = 1; i <= m; i++) {
f[u[i]][v[i]] = f[v[i]][u[i]] = 1;
}
for(int k = 1; k <= n; k++) {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
}
}
}
ll ans = 2e18;
for(int i = 1; i <= m; i++) {
ans = min(ans, 1ll * w[i] * (f[1][u[i]]+f[v[i]][n] + 1));
ans = min(ans, 1ll * w[i] * (f[1][v[i]]+f[u[i]][n] + 1));
for(int k = 1; k <= n; k++) {
ans = min(ans, 1ll * w[i] * (f[v[i]][k]+1+f[k][1]+f[k][n]+1));
ans = min(ans, 1ll * w[i] * (f[u[i]][k]+1+f[k][1]+f[k][n]+1));
}
}
cout << ans << endl;
}