Luogu P4654 [CEOI2017]Mousetrap

比较有趣的一道题,AGC做多了感觉想这种题的能力也变强了吧

首先我们要看对题目:题目要求的是最小化管理员操作的次数,而并非两者博弈进行的轮次

容易想到我们可以把陷阱设为整棵树的根,容易发现对于起点到根的路径最后老鼠是一定要走的,因此对于老鼠来说最优的决策一定是要向下走

然后我们发现,对于管理员来说,完全可以先保持旁观,因为老鼠向下走迟早会把自己卡在一个叶节点,此时管理员只需要把老鼠到根节点的路径上的其它分支都堵住,然后把老鼠到根的路径上弄脏的边都擦掉即可

考虑求出两个东西,\(up_x\)表示把\(x\)到根的路径上的所有分支都堵住的操作次数;\(f_x\)表示把老鼠卡死在\(x\)的操作次数(老鼠能走的边要么被堵住了要么被弄脏了)

\(up_x\)的转移比较简单,\(up_x=up_{fa_x}+deg_x-2+[x=st]\),其中\(st\)表示起点,因为此时管理员还需要擦掉到\(x\)的一条边

考虑\(f_x\)的转移,站在老鼠的角度,假设\(y\in son_x\),那么它肯定会选择一个\(\max f_y\)\(y\)钻进去,由于这里是管理员先操作,因此老鼠只有\(\operatorname{secondmax} f_y\)\(y\)可以钻

得到\(f_x\)的转移,\(f_x=\operatorname{secondmax}_{y\in son_x} f_y+deg_x-1\)

然后刚开始naive地以为老鼠开始只能向下走,后来WA了好久才发现老鼠可以先向上走几步然后再钻进某棵子树

刚开始想通过枚举老鼠从哪个点钻进子树,结果发现在老鼠向上的过程中管理员的操作就完全不可知,非常难处理

遂转换思路,我们发现答案显然具有可二分性,于是我们把原问题转化成判定性问题

考虑此时管理员剩下的操作步数\(t\),老鼠当前在点\(x\),枚举\(y\in son_x\),此时若\(up_x+f_y>t\)老鼠肯定会选择一头钻进去

我们再维护一个变量\(left\)表示管理员在老鼠向上跳的过程中腾出的操作数,如果\(up_x+f_y>t\)\(left=0\)那么就是老鼠的胜利,否则管理员可以通过\(left-=1\)来提前堵住这条边

综上,这道题就解决了,复杂度\(O(n\log n)\)

#include<cstdio>
#include<iostream>
#include<cctype>
#define RI register int
#define CI const int&
#define Tp template <typename T>
using namespace std;
const int N=1000005;
struct edge
{
	int to,nxt;
}e[N<<1]; int n,head[N],x,y,rt,st,cnt,ans,anc[N],f[N],up[N],deg[N]; bool tag[N];
class FileInputOutput
{
	private:
		static const int S=1<<21;
		#define tc() ((A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin)),A==B)?EOF:*A++)
		char Fin[S],*A,*B;
	public:
		Tp inline void read(T& x)
		{
			x=0; char ch; int flag=1; while (!isdigit(ch=tc())) if (ch=='-') flag=-1;
			while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc())); x*=flag;
		}
		#undef tc 
}F;
inline void addedge(CI x,CI y)
{
	e[++cnt]=(edge){y,head[x]}; head[x]=cnt; ++deg[x];
	e[++cnt]=(edge){x,head[y]}; head[y]=cnt; ++deg[y];
}
#define to e[i].to
inline void DFS(CI now,CI fa=0)
{
	int mx=0,smx=0; anc[now]=fa;
	if (now!=rt) up[now]=up[fa]+deg[now]-2+(now==st);
	for (RI i=head[now];i;i=e[i].nxt) if (to!=fa)
	{
		DFS(to,now); if (f[to]>mx) smx=mx,mx=f[to];
		else if (f[to]>smx) smx=f[to];
	}
	f[now]=smx+deg[now]-1;
}
inline bool check(int t)
{
	int left=1; for (RI now=st,i;now!=rt;now=anc[now],++left)
	{
		int dlt=0; for (i=head[now];i;i=e[i].nxt)
		if (to!=anc[now]&&!tag[to]&&up[now]+f[to]>t)
		if (!left) return 0; else ++dlt,--left; t-=dlt;
	}
	return t>=0;
}
#undef to 
int main()
{
	RI i; F.read(n); F.read(rt); F.read(st); if (st==rt) return puts("0"),0;
	for (i=1;i<n;++i) F.read(x),F.read(y),addedge(x,y);
	for (DFS(rt),i=st;i;i=anc[i]) tag[i]=1;
	int l=0,r=n-1,mid; while (l<=r)
	if (check(mid=l+r>>1)) ans=mid,r=mid-1; else l=mid+1;
	return printf("%d",ans),0;
}
posted @ 2020-12-03 08:27  空気力学の詩  阅读(90)  评论(0编辑  收藏  举报