- 给定一棵(n)个点的树,陷阱在房间(A),一只老鼠在房间(B)。
- 管理员每回合可以堵住一条边或者疏通一条边;老鼠每回合走一条边,它走过的边也会被堵住。
- 求管理员至少操作几次才能把老鼠赶到房间(A)。
- (nle10^6)
基本结论
首先,我们把陷阱所在的房间视作根节点。
显然,由于老鼠走过的边自己会堵上,那么管理员肯定有这样一个最优策略:
- 在某一时刻把老鼠堵在某个地方。
- 堵住从老鼠当前位置到根节点路径上每个节点的其他子节点。
- 放出老鼠,然后它就只能一路跑到根节点结束。
然后老鼠也有一个最优策略:
- 先向根节点走若干步(可以不走)。
- 每次选择一个能苟活最久的子树溜进去,直至被管理员堵住。
知道这些我们就可以开始做这道题了。
动态规划预处理
我们先(DP)预处理出一个(f_i)表示老鼠进入以(i)为根的子树后,管理员至少需要几步才能把老鼠赶回(i)。
显然,当老鼠在每某个节点时,管理员先操作,肯定会把(f)值最大的子树堵上,然后老鼠会跑到(f)值次大的子树中。
因此(f_i)就等于所有子节点(f)的次大值加上(i)的子节点个数(因为老鼠走的边需要疏通,老鼠没走的边需要堵住)。
二分答案
发现接下来直接做显然不太好做,主要是老鼠在向上走的过程中管理员可以同时堵住一些比较优的点。
于是我们二分答案,就可以在老鼠向上走的过程中判断出哪些子树是需要被堵上的,并判断一下步数够不够即可。
代码:(O(nlogn))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000000
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,A,B,ee,lnk[N+5],s[N+5],f[N+5];struct edge {int to,nxt;}e[2*N+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define D isdigit(c=tc())
char c,*A,*B,FI[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}F;
int fa[N+5];I void dfs(CI x)//动态规划预处理
{
RI Mx=0,Sx=0;for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^fa[x]&&
(++s[fa[e[i].to]=x],dfs(e[i].to),f[e[i].to]>Mx?(Sx=Mx,Mx=f[e[i].to]):Sx=max(Sx,f[e[i].to]));
f[x]=Sx+s[x];//次大值+子节点个数
}
int T,S[N+5],sum[N+5];I bool Check(RI k)//验证答案
{
RI i,j,c=0,t;for(i=1;i^T;++i)//老鼠向上走
{
for(t=0,j=lnk[S[i]];j;j=e[j].nxt)
e[j].to^S[i+1]&&e[j].to^S[i-1]&&sum[i]+f[e[j].to]>k&&++t;//统计需要删去的子节点个数
if((c+=t)>i||(k-=t)<0) return 0;//如果当前步数不够了或是总步数不够了
}return 1;
}
int main()
{
RI i,x,y;for(F.read(n,A,B),i=1;i^n;++i) F.read(x,y),add(x,y),add(y,x);
for(dfs(A),f[A]=0,i=B;i;i=fa[i]) S[++T]=i;for(i=T-1;i;--i) sum[i]=sum[i+1]+s[S[i]]-(i>1);//统计从一个点到根每个点有多少子节点
RI l=0,r=n-1,mid;W(l^r) Check(mid=l+r>>1)?r=mid:l=mid+1;return printf("%d
",r),0;//二分答案
}