luogu P4654 [CEOI2017]Mousetrap
这里把终点设为根方便后续处理,那么目标就是要让老鼠走到根
首先考虑老鼠动不了的情况,这种情况下可以把从这个点到终点路径上的分支堵住,然后再疏通路径上的走过的边,可以发现这是这种情况下最优的决策.在这种情况下,老鼠一定是先往上走一段距离(也可能不走),然后走进某个分支的子树,再被赶到终点.
答案显然满足二分性.对于老鼠,如果有一种策略使得花费步数\(>mid\)那么\(mid\)就不合法.所以可以从起点\(start\)往上走,每到一个点看有没有分支满足这个条件.所以还要知道进入一个子树后的花费步数,记为\(f_x\),转移的话,首先应该把\(f\)最大的儿子堵住,然后老鼠走进次大的儿子,剩下的儿子在老鼠被困住时堵住就行了,所以\(f_x=|son\ of\ x|-[|son\ of\ x|>1]+f_{x2}(son\ of\ x=\{x1,x2,...\},f_{x1}>f_{x2}>f_{x3}...)+1\).
如果进入某个分支子树,那么首先对于当前点,要把其他分支堵住,然后要把祖先的分支也堵住,所以再记分支数量\(s_x=\sum_{y\ is\ the\ ancestor\ of\ x,y\neq root} |son\ of\ y|-[y!=start]\),以及走入某个点以后到结束的操作步数\(g_x=f_x+s_{fa_x}-1\).走到某个点,如果有\(g_x>mid\)的儿子,就要尽量消耗能用的操作次数堵住,这个能用次数初始为\(1\),每次往上走一个点就\(+1\).如果还有这样的\(g_x\)就不合法.要注意每次向上走之前要把\(mid\)减去当前层的使用过的操作次数
// luogu-judger-enable-o2
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e6+10;
LL rd()
{
LL x=0,w=1;char ch=0;
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
int to[N<<1],nt[N<<1],hd[N],tot=1;
void add(int x,int y)
{
++tot,to[tot]=y,nt[tot]=hd[x],hd[x]=tot;
++tot,to[tot]=x,nt[tot]=hd[y],hd[y]=tot;
}
int n,rt,fa[N],sz[N],f[N],g[N],ans;
int st[N],tp;
void dfs(int x)
{
for(int i=hd[x];i;i=nt[i])
{
int y=to[i];
if(y==fa[x]) continue;
fa[y]=x,dfs(y);
}
tp=0;
for(int i=hd[x];i;i=nt[i])
{
int y=to[i];
if(y==fa[x]) continue;
st[++tp]=f[y];
}
sz[x]=tp;
sort(st+1,st+tp+1);
f[x]+=tp-(tp>1)+st[tp-1]+1;
}
bool ck(int mid,int xx)
{
int cn=1,la=0,dt=0;
while(xx!=rt)
{
st[tp=0]=sz[fa[xx]];
for(int i=hd[xx];i;i=nt[i])
{
int y=to[i];
if(y==fa[xx]||y==la) continue;
st[++tp]=g[y];
}
st[0]+=tp;
sort(st+1,st+tp+1);
while(tp>0&&cn>0&&st[tp]>mid) --tp,--cn,++dt;
if(st[tp]>mid) return 0;
mid-=dt,dt=0,la=xx,xx=fa[xx],++cn;
}
return 1;
}
int main()
{
n=rd(),rt=rd();
int x=rd(),la=0;
for(int i=1;i<n;++i) add(rd(),rd());
dfs(rt);
tp=0;
int xx=x;
while(xx!=rt) st[++tp]=xx,xx=fa[xx];
--sz[st[tp]];
for(int i=tp-1;i;--i) sz[st[i]]+=sz[st[i+1]]-1;
++sz[st[1]];
sz[rt]=0;
xx=x;
while(xx!=rt)
{
for(int i=hd[xx];i;i=nt[i])
{
int y=to[i];
if(y==fa[xx]||y==la) continue;
g[y]=f[y]+sz[xx]-1;
}
la=xx,xx=fa[xx];
}
int l=0,r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(ck(mid,x)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d\n",ans);
return 0;
}