2024.10.24 CW 模拟赛 B. k 短路
k 短路
2024.10.24 CW 模拟赛 T2, 有些古老, 单独拿出来写.
题意
给定带边权的简单有向图, 满足:
- 每个点至多存在于一个简单环内;
- 任意两点之间至多存在一条简单路径.
求起点 \(S\) 到终点 \(T\) 的 \(k\) 短路长度.
其中 \(n \le 50, 1 \le w \le 9, 1 \le k \le 10^{12}\).
思路
先抛一个结论: 所有可以被 \(S\) 到达, 且可以到达 \(T\) 的环, 都与路径 \(p\) 有交. 其中 \(p\) 为 \(S\) 到 \(T\) 的唯一简单路径.
证明
找到 \( S \) 到 \( T \) 的唯一简单路径 \( p \)。
假设存在在路径 \( p \) 之外的,可以被 \( S \) 到达,且可以到达 \( T \) 的环 \( c' \)。考察离 \( S, T \) 最近的 \( c' \) 中的点 \( s' \) 和 \( t' \)。
若 \( S \) 到 \( s' \) 的路径与 \( t' \) 到 \( T \) 的路径有交,则存在包含交点、\( s' \) 和 \( t' \) 的环,从而 \( s' \) 处在至少两个环中,与题设矛盾;否则,\( S \rightarrow s' \rightarrow t' \rightarrow T \) 是一条简单路径,从而 \( S \) 到 \( T \) 存在两条简单路径,与题设矛盾。
所以整张图有用的部分, 由路径 \(p\) 和其上面挂的若干个环组成.
我们可以预处理出路径 \(p\) 上所有环的边权之和, 那么问题便转化为:
- 给定 \(m \le 25\) 个物品, 所有物品的权值和 \(\le 450\), 求第 \(k\) 小完全背包的权值.
来看 \(\textrm{Subtask}\). 给定性质等价于 \(m \le 2\), 我们不妨设 \(m = 2\).
设两个物品权值分别是 \(a, b\) 并且 \(a > b\), 那么答案即为可重集 \(\{ax + by | x, y \ge 0\}\) 的第 \(k\) 小数.
我们显然可以二分答案 \(mid\), 相当于求
注意到权值 \(\le mid\) 的路径至多有 \(\displaystyle \binom{\lfloor \frac{mid}{a} \rfloor + m}{m}\) 条, 所以答案有上界 \(2a\sqrt k\), 直接枚举物品 \(a\) 出现次数即可.
再考虑 \(m \ge 3\) 的情况.
设所有物品的总权值和为 \(s\), 那么答案有上界 \(s\sqrt[m]{k} = 4.5 \times 10^6\), 直接进行背包 DP 即可.
代码
void dfs(int u) {
for (auto [v, w] : e[u]) {
if (dep[v]) {
val[v] = dep[u] - dep[v] + w;
continue;
}
fa[v] = u, dep[v] = dep[u] + w;
dfs(v);
}
}
void init() {
read(n, m, k, S, T);
for (int i = 1, u, v, w; i <= m; ++i) {
read(u, v, w);
e[u].emplace_back(v, w);
}
dep[S] = 1, dfs(S), --k;
int x = T;
while (x) {
if (val[x]) rng.push_back(val[x]);
x = fa[x];
}
}
void calculate() {
if (rng.empty()) {
if (k or !dep[T]) puts("-1");
else print(dep[T] - 1);
}
else if (rng.size() == 1)
print(dep[T] - 1 + k * rng.front());
else if (rng.size() == 2) {
++k;
int a = rng[0], b = rng[1];
if (a < b) swap(a, b);
long long l = 0, r = 1e18, mid, ans = r, s;
while (l <= r) {
mid = (l + r) >> 1;
s = 0;
for (int i = 0; i <= 2e6; ++i) {
if (a * i > mid) break;
s += (mid - a * i) / b + 1;
if (s >= k) break;
}
if (s >= k) r = (ans = mid) - 1;
else l = mid + 1;
}
print(ans + dep[T] - 1);
}
else {
int ans = 0;
f[0] = 1;
for (int x : rng)
for (int i = x; i <= M - 10; ++i) f[i] += f[i - x];
for (int i = 0; i <= M - 10; ++i) {
if (i) f[i] += f[i - 1];
if (f[i] > k) {
ans = i;
break;
}
}
print(ans + dep[T] - 1);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现