【牛逼做法】P4186 Cow at Large
-
题面 : click
-
题意 : 农场为一颗 \(n\) 个结点的无根树, 奶牛出现在位置 \(k\) 的谷仓, 有一些出口谷仓, 且其仅与一个谷仓相连。一开始所有出口谷仓都有一个农民, 奶牛农民每个单位时间内可以移动到相邻的谷仓。奶牛的目标是移动到一个出口谷仓, 如果农民与奶牛相遇,那么他就会抓住奶牛。请问至少要多少农民才可以让奶牛无法到达任何一个出口谷仓。 数据范围 :\(n\leq 10^5\)
-
一些想法 : 显然, 先钦定 \(k\) 为根, \(dep_k = 0\)。
前置结论 \(0.5\) : 结点 \(u\) 农民最多走到他的 \(dep_u / 2\) 级别祖先 (向下取整)。
我们称一个农民从一个叶子节点比奶牛 先 走到一个节点, 则他 统治 了该结点及其子树。若是一个节点 \(u\) 及其子树被统治了是优美的, 因为这样奶牛就无法到达 \(u\) 及其子树内的任何一个结点 (结论 \(1\))。于是我们可以考虑求出一个农民向爸爸走最远统治的结点 (结论 \(2\)), 并给其打上了标记。然后再做一遍 \(dfs\), 看看奶牛到每一个叶子上会经过多少个 被统治 的结点即可 (结论 \(2\))。
\(Tips\) : 如果经过了被统治的节点后, 就要停止 \(dfs\). -
正确性证明 :
前置结论 \(0.5\) : 无需证明, 画图即可。
结论 \(1\) : 显然, 农夫可以在原地守着不动, 奶牛就进不去 (老六了属于是)。
结论 \(2\) : 我们如果撤掉了一个被统治的结点 \(u\), \(u\) 子树 肯定 要再安放 至少 一个被统治的节点,否则奶牛可以达到 \(u\) 子树内的任意一个叶子, 故选择统治 \(u\) 结点不会更劣, 若是在 \(dfs\) 过程中遇到了 \(u\), 那么也同理。 -
咋做 :
- 我会暴力 !显然我们可以暴力找到 \(dep_u / 2\) 级别祖先, 然后打标记。$ O(n ^ 2)$
- 我会 倍增 / 长剖 转化为 \(k\) 级祖先。由 \(CF208E\) 的特殊 \(Tricks\) 告诉我可以离线 \(dfs\), 然后取栈上 \(k\) 位前的结点。 \(O(n \log n)\) 、 \(O(n)\)
- 排序优化 显然, 我们发现, 一个节点的深度更小, 那么跳到的结点深度也会更小, 所以我们排序, 然后暴力向上跳打标记, 跳到目标节点停止。 如果中间遇到被打标记的结点则不跳, 因为有跳的更高的节点了,所以每个节点都只会经过一次 \(O(n \log n)\)
正确性 : 一个点 \(u\) 会跳到 \(dep_u - dep_u / 2\) 的位置, 比一下深度就知道那个更优了。
3.5 $ final : $ 因为深度的上界为 \(n\), 所以可以把叶子存入相应深度的 \(vector\) 里面, 然后深度从小到大遍历一遍即可。 \(O(n)\)
- 代码 (蛮简单的):
#include <bits/stdc++.h>
using namespace std;
inline int read () {
int ans = 0; char c = getchar(), last = ' ';
while(c < '0' || c > '9') last = c,c = getchar();
while(c >= '0' && c <= '9') ans = (ans << 1) + (ans << 3) + c - '0',c = getchar();
return last == '-' ? - ans : ans;
}
const int N = 1e5 + 5;
vector <int> EDGE[N], st[N];
int n, k, ans, dep[N], f[N], lf[N], fail[N];
inline void dfs1 (int u, int fa) {
lf[fa] = 1, dep[u] = dep[fa] + 1, f[u] = fa; // 计算必要数据
for (auto v : EDGE[u]) if (v != fa) dfs1(v, u);
}
inline void dfs2 (int u, int fa) {
if (fail[u]) { ans ++; return ; } // 遇到被统治的结点, 更新答案。
for (auto v : EDGE[u]) if (v != fa) dfs2(v, u);
}
int main() {
n = read(), k = read();
for (int i = 1, u, v; i < n; i ++) {
u = read(), v = read();
EDGE[u].push_back(v), EDGE[v].push_back(u);
}
dep[0] = -1, dfs1(k, 0);
for (int i = 1; i <= n; i ++) if (! lf[i]) st[dep[i]].push_back(i); // 存入对应深度的 vector
for (int i = 0; i < n; i ++) {
if (! st[i].size()) continue ;
for (int j = 0; j < st[i].size(); j ++) {
int pos = st[i][j], tim = 0;
while(tim <= dep[pos] && ! fail[pos]) tim ++, fail[pos] = 1, pos = f[pos]; // 暴力跳爸爸
}
}
dfs2(k, 0), cout << ans;
}

浙公网安备 33010602011771号