P4654 [CEOI2017] Mousetrap

\(\mathcal Link\)

为了方便,以目标为根,向深度浅的位置走为“向上走”,否则为“向下走”。

考虑到老鼠一旦开始向下走,它就一定会一直向下,直到走到叶子或者唯一向下的路被堵住了,此时只需要封住所有岔路并擦干即可。

\(f_i\) 表示将老鼠从 \(i\) 赶回 \(i\) 的最优解。

首先,\(i\) 的所有子树必须被封上/擦干一次,否则老鼠蹿上来后又回进入其他子树。
由于博弈,所以老鼠会选择子树中 \(f\) 大的走,由于只能封一个,所以会选次大的走(不存在的话路直接就给堵死了,为 \(0\)

\(f_u=\operatorname{2ndmax}\limits_{v\in son_u} f_v+\deg u-1\)

考虑设 \(h(u)\) 表示 \(u\rightarrow rt\) 的所有岔边,\(g_u\) 表示从进入 \(u\) 到回到根的最优解。

由于如果存在“向上走”,则有一条边会不用堵。

\(g_u=h(fa_u)+f_u+[fa_u=m]\)

接下来的问题是不清楚老鼠会向上跳到哪里,所以考虑二分答案,转变为输赢博弈(小于等于管理员胜,否则老鼠胜)。

由于堵住的路不能疏通,所以我们只能堵住所有岔路,无法阻止老鼠上跳。

考虑在老鼠上跳的过程中,封住所有可能导致失败的子树。如果太多导致次数不够或者手速太慢就会失败。

有一个细节,就是 \(u\) 的某个儿子被封上了,不能直接让 \(x\) 减去一,必须等到所有子树做完后再统一减。
原因在于如果老鼠进入了 \(u\) 的其它子树 \(v\) ,那么由于回到根时 \(u\) 的所有儿子都应该被堵上,这个贡献被 \(g_v\) 多算了一次 ,所以 \(x\) 减后 \(g_v\) 也会减同样的值,相当于没减。这就是为什么 check 中会出现 tmp

View Code
#include <cstdio>
#include <algorithm>
#include <cctype>
using namespace std;
char buf[1<<14],*p1=buf,*p2=buf;
#define GetC() ((p1==p2)&&(p2=(p1=buf)+fread(buf,1,1<<14,stdin),p1==p2)?EOF:*p1++)
struct Ios{}io;
template <typename _tp>
Ios &operator >>(Ios &in,_tp &x){
	x=0;int w=0;char c=GetC();
	for(;!isdigit(c);w|=c=='-',c=GetC());
	for(;isdigit(c);x=x*10+(c^'0'),c=GetC());
	if(w) x=-x;
	return in;
}
const int N=1e6+5;
int n,t,m;
struct EDGE{ int nxt,to; }e[N<<1];
int head[N],tot;
void add(int from,int to){
	e[++tot].nxt=head[from];
	e[tot].to=to;
	head[from]=tot;
}
int dp[N],deg[N],h[N],f[N],g[N];
int st[N],top;
void dfs(int u,int fa){
	f[u]=fa;
	dp[u]=0;
	int mx1=0,mx2=0;
	if(fa) h[u]=h[fa]+deg[u]-1-(fa!=0);
	else h[u]=0;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa) continue ;
		dfs(v,u);
		if(dp[v]>=mx1) mx2=mx1,mx1=dp[v];
		else mx2=max(mx2,dp[v]);
	}
	dp[u]=mx2+deg[u]-1;
	if(fa==0) dp[u]=0;
}
bool check(int x){
	int sum=0,tmp=0;
	for(int i=1;i<top;++i){
		int u=st[i];
		tmp=0;
		for(int _=head[u];_;_=e[_].nxt){
			int v=e[_].to;
			if(v==st[i+1]||v==st[i-1]) continue ;
			if(dp[v]>x) ++tmp;
		}
		sum+=tmp;x-=tmp;
		if(x<0||sum>i) return false;
	}
	return true;
}
int main(){
	io>>n>>t>>m;
	if(t==m){
		puts("0");
		return 0;
	}
	for(int i=1;i<n;++i){
		int u,v;io>>u>>v;
		add(u,v);add(v,u);
		++deg[u];++deg[v];
	}
	dfs(t,0);
	for(int i=1;i<=n;++i){
		if(i!=t){
			dp[i]+=h[f[i]]+(f[i]==m);
		}
	}
	for(int i=m;i;i=f[i]) st[++top]=i;
	int l=0,r=2*n;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid)) r=mid-1;
		else l=mid+1;
	}
	printf("%d\n",l);
	return 0;
}
posted @ 2023-01-09 15:14  pref_ctrl27  阅读(34)  评论(0编辑  收藏  举报