[CEOI 2017] Mousetrap
挺有意思的一道题。
记毛毛虫为一条路径上除了底部所有点的不在路径上的子节点与该节点形成的边集。
首先设陷阱为根 \(rt\) 可以简化问题。
假设 \(m\) 是 \(rt\) 的儿子,则老鼠只会走到 \(m\) 的二儿子,大概流程就是先走到底,然后管理员把路径上毛毛虫的边都堵住,这样一定比老鼠回来路上走过去优,然后再清理这条路径送老鼠进陷阱。设 \(f_u\) 表示从 \(u\) 开始再赶回 \(u\) 的最小操作数,那么管理员肯定在老鼠没开始走的时候就堵住 \(f_v\) 最大的 \(v\),于是老鼠选次大的走,即 \(f_u = \text{2ndmax}f_v + son_u\),因为所有子节点都会被操作一次,\(v\) 是清理其它是堵住。
但是如果 \(m\) 不是 \(rt\) 儿子就不一样了,老鼠可以先向上走到一个点 \(u\),再找一个 \(u\) 的儿子 \(v\) 走,那还要多堵 \(u\) 到 \(rt\) 路径上毛毛虫的边,记为 \(sum_u\),显然 \(sum_u = sum_{fa_u} + son_u - [u \neq m]\)。那么操作次数就是 \(sum_u + f_v\)。
这个东西不是很好贪心或 DP,又发现答案有单调性,于是可以二分答案,老鼠每次上去前就有一次操作可以堵一个老鼠没走过的 \(v\),如果不能堵了就不合法。会堵一个 \(v\) 当且仅当 \(sum_u + f_v >\) 当前操作次数。
时间复杂度 \(\mathcal{O}(n \log n)\)。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
// typedef __int128 i128;
typedef pair<int, int> pii;
const int N = 1e6 + 10, mod = 998244353;
template<typename T>
void dbg(const T &t) { cout << t << endl; }
template<typename Type, typename... Types>
void dbg(const Type& arg, const Types&... args) {
cout << arg << ' ';
dbg(args...);
}
namespace Loop1st {
int n, rt, m, fa[N], f[N], sum[N], son[N], b[N];
basic_string<int>e[N];
void dfs(int u) {
int mx = 0, cmx = 0;
for (int v : e[u]) if (v != fa[u]) {
fa[v] = u;
son[u]++;
dfs(v);
if (f[v] > mx) cmx = mx, mx = f[v];
else if (f[v] > cmx) cmx = f[v];
}
f[u] = cmx + son[u];
}
bool check(int x) {
int cnt = 1;
for (int u = m; u != rt; u = fa[u], cnt++) {
int val = 0;
for (int v : e[u]) if (v != fa[u] && !b[v] && f[v] + sum[u] > x) {
if (!cnt) return 0;
val++; cnt--;
}
x -= val;
}
return x >= 0;
}
void main() {
cin >> n >> rt >> m;
for (int i = 1, u, v; i < n; i++) {
cin >> u >> v;
e[u] += v; e[v] += u;
}
dfs(rt);
for (int u = 1; u <= n; u++) sum[u] = sum[fa[u]] + son[u] - 1 + (u == m);
for (int u = m; u != rt; u = fa[u]) b[u] = 1;
int L = -1, R = n * 2 + 1;
while (L + 1 < R) {
int mid = (L + R) >> 1;
if (check(mid)) R = mid;
else L = mid;
}
cout << R << '\n';
}
}
int main() {
// freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T = 1;
// cin >> T;
while (T--) Loop1st::main();
return 0;
}

浙公网安备 33010602011771号