Codeforces 1578L. Labyrinth
Codeforces 1578L. Labyrinth
第一眼是一个图论题, 感觉很难很难. 于是考虑发现性质.
引理 0 起点可以是除了 \(1\) 的任何点.
证明 设一开始身体的宽度为 \(w\) , 则对于任一点 \(u\) , 一定存在一条路径 \(1\rightarrow u\) 满足路径上的边权都 \(\ge w\) . 于是可以先不吃糖走到任一节点 \(u\) 再开始.
引理 1 选的边一定在原图的最大生成树上.
证明 使用 \(\text{Kruskal}\) 算法流程便可推出这个结论.
于是问题就转化成了一个树上问题, 看起来简单多了.
引理 2 对于当前树上最小的边, 删去这条边后剩下两个连通块, 一定是先吃完一个连通块的点, 再走过这条边, 并吃完另一个连通块的点.
证明 我们假设删除这条边剩下的连通块为 \(u, v\) . 假设我们先吃掉连通块 \(u\) 的一部分, 然后走过这条边, 再吃完连通块 \(v\) , 最后再走回来吃完 \(u\) .
如果这种方案可行, 那么一定也可以先吃完连通块 \(v\) , 然后走过这条边, 再吃完连通块 \(u\) . 并且由于吃东西只会使身体变宽, 这一种方案肯定比上一种更优.
因此, 我们可以把原树转化为一个 \(\text{Kruskal}\) 重构树. 连通块问题也就转化成了子树问题.
设 \(f_u\) 表示以 \(u\) 为根的子树中, 当前在 \(u\) 位置, 当前最多可以多胖, 使得可以吃完该子树. 令 \(s_u\) 表示以 \(u\) 为根的子树内所有点(虚点)的权值和.
假设现在有一条权值为 \(v\) 的边合并了 \(x,y\) 两颗子树, 并在 \(\text{Kruskal}\) 重构树上新建了节点 \(z\) , 那么如果先吃 \(x\) 子树, 则必须满足三个条件:
-
可以吃完子树 \(x\) . 于是 \(f_z \le f_x\) ;
-
吃完子树 \(x\) 后可以到 \(z\) 节点, 并经过 \(z\) . 于是 \(f_z+s_x\le v\) ;
-
吃完子树 \(x\) 后可以继续吃掉子树 \(y\) . 于是 \(f_z+s_x\le f_y\) .
所以转移为 \(\min\{v,\,f_y,\,f_x+s_x\}-s_x\rightarrow f_z\) .
还有可能先吃掉 \(y\) 子树, 于是取 \(\max\) 即可.
总时间复杂度 \(\mathcal O(n\log n)\)
参考代码
#include <bits/stdc++.h>
using namespace std;
static constexpr int64_t inf = 0x3f3f3f3f3f3f3f3f;
static constexpr int Maxn = 2e5 + 5, Maxm = 1e5 + 5;
int n, m, en, fa[Maxn];
int64_t a[Maxn], s[Maxn], dp[Maxn];
struct Edge {
int u, v;
int64_t w;
Edge() = default;
friend bool operator < (const Edge &lhs, const Edge &rhs) {
return lhs.w > rhs.w;
}
} e[Maxm];
int fnd(int x) { return fa[x] == x ? x : fa[x] = fnd(fa[x]); }
int main(void) {
scanf("%d%d", &n, &m); en = n;
for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
for (int i = 1; i <= m; ++i) scanf("%d%d%lld", &e[i].u, &e[i].v, &e[i].w);
sort(e + 1, e + m + 1);
for (int i = 1; i <= n; ++i) fa[i] = i, s[i] = a[i];
memset(dp, inf, sizeof(dp));
for (int i = 1; i <= m; ++i) {
int u = e[i].u, v = e[i].v;
int64_t w = e[i].w;
u = fnd(u), v = fnd(v);
if (u == v) continue;
++en;
fa[u] = fa[v] = fa[en] = en;
s[en] = s[u] + s[v];
dp[en] = max(min(w, dp[u]) - s[v], min(w, dp[v]) - s[u]);
}
if (dp[en] <= 0) dp[en] = -1;
printf("%lld\n", dp[en]);
exit(EXIT_SUCCESS);
} // main
浙公网安备 33010602011771号