Solution -「Gym 101630J」Journey from Petersburg to Moscow
\(\mathscr{Description}\)
Link.
给定含有 \(n\) 个点 \(m\) 条边的带权无向图,一条路径的长度定义为其中前 \(k\) 大的边权和,求 \(1\) 到 \(n\) 的最短路。
\(n,m,k\le3\times10^3\)。
\(\mathscr{Solution}\)
模拟赛题,我对放 WQS 过了但其实大样例都没有凸性的造数据人表示 😅。
先把正常的最短路作为答案上界,接下来我们只需要考虑路径中包含至少 \(k\) 条边的情况。算是一种构造,枚举第 \(k\) 大的边权 \(w_0\),令所有边权减去 \(w_0\) 并向 \(0\) 取 \(\max\),用此时最短路的长度 \(+kw_0\) 更新答案。
理解:大于 \(0\) 的边相当于这条边会加入答案贡献,那么超过 \(k\) 条一定不优;小于 \(k\) 条虽然不合法,但是一定会被 \(w_0\) 变小的方案覆盖掉。
最终复杂度就是 \(\mathcal O(m^2\log m)\) 的。
\(\mathscr{Code}\)
/*+Rainybunny+*/
#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
#define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)
typedef long long LL;
typedef std::pair<LL, int> PLI;
typedef std::pair<int, int> PII;
#define fi first
#define se second
const int MAXN = 3e3, MAXM = 3000;
const LL LINF = 1ll << 60;
int n, m, K, S, T, eu[MAXM + 5], ev[MAXM + 5], ew[MAXM + 5];
std::vector<PII> adj[MAXN + 5];
LL dis[MAXN + 5];
inline void dijkstra(const int dlt) {
static std::priority_queue<PLI, std::vector<PLI>, std::greater<PLI> > heap;
rep (i, 1, n) dis[i] = LINF;
heap.push({ dis[S] = 0, S });
while (!heap.empty()) {
PLI p(heap.top()); heap.pop();
if (p.fi != dis[p.se]) continue;
for (auto& [v, w]: adj[p.se]) {
int nw = std::max(w - dlt, 0);
if (dis[v] > nw + p.fi) {
heap.push({ dis[v] = nw + p.fi, v });
}
}
}
}
int main() {
// freopen("fee.in", "r", stdin);
// freopen("fee.out", "w", stdout);
std::ios::sync_with_stdio(false), std::cin.tie(0);
std::cin >> n >> m >> K, S = 1, T = n;
rep (i, 1, m) {
std::cin >> eu[i] >> ev[i] >> ew[i];
adj[eu[i]].emplace_back(ev[i], ew[i]);
adj[ev[i]].emplace_back(eu[i], ew[i]);
}
dijkstra(0);
LL ans = dis[T];
rep (i, 1, m) {
dijkstra(ew[i]);
ans = std::min(ans, dis[T] + 1ll * ew[i] * K);
}
std::cout << ans << '\n';
return 0;
}