Solution -「CEOI 2017」「洛谷 P4654」Mousetrap
\(\mathscr{Description}\)
Link.
在一个含 \(n\) 个结点的树形迷宫中,迷宫管理者菈米莉丝和一只老鼠博弈。老鼠初始时在结点 \(y\),有且仅有结点 \(x\) 布置有陷阱。一条边有切断,脏和干净三种状态,初始时所有边是干净的,每一回合中:
- 管理者先行动:选择一条脏或干净的边,将其切断;选择一条脏的边,将其清理干净;或者不进行任何操作,此时管理者所用的操作次数不变。
- 老鼠后行动:设当前老鼠在结点 \(u\),则选择一条干净的边 \((u,v)\),走到 \(v\);当且仅当不存在这样的 \((u,v)\) 时,不进行任何操作。
当老鼠走到 \(x\),游戏结束。管理者希望自己的操作次数,老鼠希望最大化管理者的操作次数,求双方最优决策下管理者的操作次数。
\(n\le10^6\)。
\(\mathscr{Solution}\)
不妨以 \(x\) 为根。若老鼠从 \(u\) 走向了 \(u\) 的某个叶子 \(v\),那么在管理者不帮忙清理 \((u,v)\) 的情况下,老鼠不能行动。这个时候管理者显然应当切断路径 \((v,x)\) 上的所有分叉边,并清理干净路径 \((v,x)\) 上的边,此后老鼠不得不走向毁灭。可见,老鼠有且仅有一次向孩子走的机会,然后就只能在子树内逃窜,最后被赶上 \(x\)。
一般的,设 \(f(u)\) 表示老鼠自觉从起点 \(y\) 走到 \(u\),且已经向孩子走过时,把老鼠赶到 \(x\) 需要的最优操作次数。不妨设老鼠从 \(y\) 向上走,在点 \(w\) 第一次向孩子走;路径 \((u,x)\) 上的分叉边数量为 \(b\);点 \(u\) 的深度为 \(d_u\);\(\operatorname{smax} S\) 表示集合 \(S\) 中的次大值,那么
d_u-d_w+b&u\text{ is a leaf}\\
d_u-d_w+b+1&u\text{ has exactly one child}\\
\operatorname{smax}_{v\in\operatorname{son}(u)}\{f(v)\}&\text{otherwise}
\end{cases}.
\]
此后的问题便是决定老鼠究竟在哪个 \(w\) 向孩子走。尝试二分答案为 \(x\),这个 \(x\) 需要应对两种开销:
- 在老鼠还未向孩子走时,强制老鼠向上走;
- 在老鼠向孩子走之后,完成计算 \(f\) 时的讨论。
因此,模拟老鼠从 \(y\) 向上走的过程,若当前走到 \(w\),\(w\) 存在若干个在 \(x\) 步操作内无暇应付的孩子,管理者就需要把它们全部切掉,但是老鼠每走一步,管理者只能再切一条边。据此判断 \(x\) 是否合法即可。
最终复杂度为 \(\mathcal O(n\log n)\)。
\(\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)
inline char fgc() {
static char buf[1 << 17], *p = buf, *q = buf;
return p == q && (q = buf + fread(p = buf, 1, 1 << 17, stdin), p == q) ?
EOF : *p++;
}
template <typename Tp = int>
inline Tp rint() {
Tp x = 0, s = fgc(), f = 1;
for (; s < '0' || '9' < s; s = fgc()) f = s == '-' ? -f : f;
for (; '0' <= s && s <= '9'; s = fgc()) x = x * 10 + (s ^ '0');
return x * f;
}
const int MAXN = 1e6;
int n, rt, st, fa[MAXN + 5], dep[MAXN + 5], f[MAXN + 5];
std::vector<int> adj[MAXN + 5], brv[MAXN + 5];
bool onp[MAXN + 5];
inline void init(const int u) {
for (int v: adj[u]) if (v != fa[u]) {
dep[v] = dep[fa[v] = u] + 1, init(v);
}
}
inline void calcF(const int u, int top, int brc) {
if (onp[u]) top = u;
brc += adj[u].size() - 1 - (adj[u].size() > 1);
if (adj[u].size() == 1) return void(f[u] = dep[u] - dep[top] + brc);
int mx = 0, sx = 0;
for (int v: adj[u]) if (v != fa[u]) {
calcF(v, top, brc);
if (f[v] > mx) sx = mx, mx = f[v];
else if (f[v] > sx) sx = f[v];
}
if (adj[u].size() == 2) f[u] = dep[u] - dep[top] + brc + 1;
else f[u] = sx;
}
inline bool check(const int x) {
int ban = 0, alw = 0;
for (int u = st; u != rt; u = fa[u]) {
for (int b: brv[u]) ban += b + ban - (u != st) > x;
if (ban > ++alw) return false;
}
return true;
}
int main() {
n = rint(), rt = rint(), st = rint();
rep (i, 2, n) {
int u = rint(), v = rint();
adj[u].push_back(v), adj[v].push_back(u);
}
if (rt == st) return puts("0"), 0;
init(rt), onp[rt] = true;
for (int u = st; u != rt; onp[u] = true, u = fa[u]);
for (int v: adj[rt]) if (onp[v]) calcF(v, 0, 0);
for (int u = st; u != rt; u = fa[u]) {
for (int v: adj[u]) if (!onp[v]) brv[u].push_back(f[v]);
std::sort(brv[u].begin(), brv[u].end());
}
int l = f[st], r = n << 1;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
printf("%d\n", l);
return 0;
}
Solution -「CEOI 2017」「洛谷 P4654」Mousetrap的更多相关文章
- 「区间DP」「洛谷P1043」数字游戏
「洛谷P1043」数字游戏 日后再写 代码 /*#!/bin/sh dir=$GEDIT_CURRENT_DOCUMENT_DIR name=$GEDIT_CURRENT_DOCUMENT_NAME ...
- [CodePlus 2017 11月赛&洛谷P4058]木材 题解(二分答案)
[CodePlus 2017 11月赛&洛谷P4058]木材 Description 有 n棵树,初始时每棵树的高度为 Hi ,第 i棵树每月都会长高 Ai.现在有个木料长度总量为 S的订单, ...
- Solution -「JSOI 2019」「洛谷 P5334」节日庆典
\(\mathscr{Description}\) Link. 给定字符串 \(S\),求 \(S\) 的每个前缀的最小表示法起始下标(若有多个,取最小的). \(|S|\le3\time ...
- Solution -「洛谷 P4372」Out of Sorts P
\(\mathcal{Description}\) OurOJ & 洛谷 P4372(几乎一致) 设计一个排序算法,设现在对 \(\{a_n\}\) 中 \([l,r]\) 内的元素排 ...
- Solution -「POI 2010」「洛谷 P3511」MOS-Bridges
\(\mathcal{Description}\) Link.(洛谷上这翻译真的一言难尽呐. 给定一个 \(n\) 个点 \(m\) 条边的无向图,一条边 \((u,v,a,b)\) 表示从 ...
- Solution -「APIO 2016」「洛谷 P3643」划艇
\(\mathcal{Description}\) Link & 双倍经验. 给定 \(n\) 个区间 \([a_i,b_i)\)(注意原题是闭区间,这里只为方便后文描述),求 \(\ ...
- 「洛谷4197」「BZOJ3545」peak【线段树合并】
题目链接 [洛谷] [BZOJ]没有权限号嘤嘤嘤.题号:3545 题解 窝不会克鲁斯卡尔重构树怎么办??? 可以离线乱搞. 我们将所有的操作全都存下来. 为了解决小于等于\(x\)的操作,那么我们按照 ...
- 「洛谷3338」「ZJOI2014」力【FFT】
题目链接 [BZOJ] [洛谷] 题解 首先我们需要对这个式子进行化简,否则对着这么大一坨东西只能暴力... \[F_i=\sum_{j<i} \frac{q_iq_j}{(i-j)^2}-\s ...
- 「BZOJ2733」「洛谷3224」「HNOI2012」永无乡【线段树合并】
题目链接 [洛谷] 题解 很明显是要用线段树合并的. 对于当前的每一个连通块都建立一个权值线段树. 权值线段树处理操作中的\(k\)大的问题. 如果需要合并,那么就线段树暴力合并,时间复杂度是\(nl ...
随机推荐
- react中异步组件以及withRouter的使用
什么是异步组件?简单来说就是异步加载一个组件,正常情况浏览器加载的是我们打包好的bundle.js文件,那么这个文件是集合了所有js是代码,然而我们首屏加载并不需要一次性加载所有的组件,这会造成性能的 ...
- 自动化集成:Pipeline整合Docker+K8S
前言:该系列文章,围绕持续集成:Jenkins+Docker+K8S相关组件,实现自动化管理源码编译.打包.镜像构建.部署等操作:本篇文章主要描述流水线集成K8S用法. 一.背景描述 分布式服务的部署 ...
- JAVA-JDK1.7-ConCurrentHashMap-源码并且debug说明
概述 在一个程序员的成长过程就一定要阅读源码,并且了解其中的原理,只有这样才可以深入了解其中的功能,就像ConCurrentHashMap 是线程安全的,到底是如何安全的?以及如何正确使用它?reha ...
- Enumy:一款功能强大的Linux后渗透提权枚举工具
Enumy是一款功能强大的Linux后渗透提权枚举工具,该工具是一个速度非常快的可移植可执行文件,广大研究人员可以在针对Linux设备的渗透测试以及CTF的后渗透阶段利用该工具实现权限提升,而Enum ...
- java多态转型II
1 package face_09; 2 3 /* 4 * 毕老师和毕姥爷的故事. 5 */ 6 class 毕姥爷 { 7 void 讲课() { 8 System.out.println(&quo ...
- .NET 7 预览版来啦,我升级体验了
听说.NET 7 来了,站长怎能不尝鲜呢,在除夕当天将体验情况简单汇报下,然后迎新春喽: 本文目录 .NET 7 详情(Proposed .NET 7 Breaking Changes #7131) ...
- Vue 之 浏览本地图片功能
template <input type="file" ref="input_file" @change="fileChange" ...
- Elasticsearch (1) 文档操作
本文介绍如何在Elasticsearch中对文档进行操作. 1.检查Elasticsearch及Kibana运行是否正常 在浏览器输入192.168.6.16:9200,有如下输出则说明Elastic ...
- Understanding C++ Modules In C++20 (2)
Compiling evironment: linux (ubuntu 16.04)+ gcc-10.2. The post will focus on using export,import,vis ...
- Atcoder ARC-060
ARC060(2020.7.8) A 背包板子 B 首先感觉这个东西应该不能直接 \(O(1)\) 算出来,那么复杂度应该就是 \(O(\log n), O(\sqrt{n}), O(\sqrt{n} ...