CEOI2017 Mousetrap

题目描述

有一个有 n n n 个房间和 n − 1 n-1 n1 条走廊的迷宫,保证任意两个房间可以通过走廊互相到达,换句话说,这个迷宫的结构是一棵树。

一个老鼠被放进了迷宫,迷宫的管理者决定和老鼠做个游戏。

一开始,有一个房间被放置了陷阱,老鼠出现在另一个房间。老鼠可以通过走廊到达别的房间,但是会弄脏它经过的走廊。老鼠不愿意通过脏的走廊。

每个时刻,管理者可以进行一次操作:堵住一条走廊使得老鼠不能通过,或者擦干净一条走廊使得老鼠可以通过。然后老鼠会通过一条干净的并且没被堵住的走廊到达另一个房间。只有在没有这样的走廊的情况下,老鼠才不会动。一开始所有走廊都是干净的。管理者不能疏通已经被堵住的走廊。

现在管理者希望通过尽量少的操作将老鼠赶到有陷阱的房间,而老鼠则希望管理者的操作数尽量多。请计算双方都采取最优策略的情况下管理者需要的操作数量。

注意:管理者可以选择在一些时刻不操作。

输入输出格式

输入格式:

第一行三个空格隔开的正整数数 n,t,mn,t,m。分别代表房间的个数,陷阱房的编号和老鼠起始房间的编号。

接下来 n − 1 n-1 n1 行,每行两个空格隔开的整数 a i , b i a_i,b_i ai,bi ​​,表示有一条走廊连接编号为 a i a_i ai​ 和 b i b_i bi​​ 的房间。

输出格式:

输出一行包含一个整数,表示双方都采取最优策略的情况下,管理者需要的操作数量。

思路

本来做Chase的时候就有把CEOI2017做完的想法,结果咕咕了。然后考试又考了一道,QwQ。痛下决心一定要做完。

题目较难, 题解就贴这位巨佬的。

想了很久。我的想法跟这个老哥很像,但我一开始觉得老鼠向上爬只会去 f f f值最大的子树,就直接把答案算成该子树的 f f f值。然后,LOJ的数据60+。

反例就是,管理者再上方封路后,老鼠走次小子树(吗)。

代码

#include<cstdio>
#include<string>
#define R_ register
inline int read() {
	int ret=0,f=1,ch=getchar();
	for (; !isdigit(ch); ch=getchar()) if (ch=='-') f=-f;
	for (; isdigit(ch); ch=getchar()) ret=ret*10+ch-48;
	return ret*f;
}
const int maxn=2e6+5;
int tot,top,son[maxn],nxt[maxn],lnk[maxn],rod[maxn];
int N,T,S,Ans,f[maxn],fa[maxn],deg[maxn],sum[maxn],vis[maxn];
inline void add_edge(int x,int y) {
	son[++tot]=y,nxt[tot]=lnk[x],lnk[x]=tot,++deg[x];
	son[++tot]=x,nxt[tot]=lnk[y],lnk[y]=tot,++deg[y];
}
void Build(int x,int pre=0) {
	R_ int k=lnk[x],v,Max1=0,Max2=0;
	for (; v=son[k],k; k=nxt[k]) if (v^pre)
		if (fa[v]=x,Build(v,x),f[v]>=Max1) Max2=Max1,Max1=f[v];
		else if (f[v]>Max2) Max2=f[v];
	f[x]=Max2+deg[x]-1;
}
inline bool check(int res) {
	int cnt=0,tmp;
	for (R_ int i=1,k,v; i<=top; ++i) {
		for (tmp=0,k=lnk[rod[i]]; v=son[k]; k=nxt[k])
			if (v^rod[i+1]&&v^rod[i-1]&&sum[i]+f[v]+1-(i!=1)>res) ++tmp;
		cnt+=tmp,res-=tmp;
		if (res<0||cnt>i) return 0;
	}
	return 1;
}
int main() {
#ifndef ONLINE_JUDGE
	freopen("trap.in","r",stdin);
	freopen("trap.out","w",stdout);
#endif
	R_ int i,L=0,R=1e8,mid;
	for (N=read(),T=read(),S=read(),i=1; i<N; ++i) add_edge(read(),read());
	for (Build(T),f[T]=0,i=S; i; i=fa[i]) rod[++top]=i;
	for (i=top-1; i; i--) sum[i]=sum[i+1]+deg[rod[i]]-2;
	for (; L<=R; ) check(mid=L+R>>1)?Ans=mid,R=mid-1:L=mid+1;
	return printf("%d\n",Ans),0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值