首页 > 其他分享 >[CEOI2017] Mousetrap

[CEOI2017] Mousetrap

时间:2023-09-17 20:34:13浏览次数:41  
标签:son int ed CEOI2017 st now Mousetrap dp

[CEOI2017] Mousetrap
策略其实比较好想但是把式子列出来有点难。
不妨把陷阱房作为根,这样就只用把老鼠往上赶。
设起始房为 st,陷阱房为 ed。
考虑 st 是 ed 的子节点,老鼠不可能送死所以会往子节点走,而管理员的最优策略是老鼠边走边堵。
直到老鼠动不了时,设在节点 x,把 x 到 ed 路径上所有点到未脏子节点的走廊堵住然后打扫。
设 \(dp_x\) 表示从 x 走到子节点最后回到 x 的最小操作数,\(son_x\) 表示 x 的儿子数,则

\[dp_x=\sum_{y\in son_x}second \max(dp_y)+son_x \]

因为老鼠要操作尽量大,所以首先堵 dp 值最大的,要把走的那条走廊打扫,其他堵住,所以是 \(son_x\)。
设 \(f_x\) 表示把 x 到 ed 路径上节点的子节点堵住的操作数,递推可得

\[f_x=f_fax+son_x-1+(x=st) \]

如果 x 不为 st 那么往上走时的那条边就不用堵了。
则 x 走儿子 y 的花费为 \(f_x+dp_y\)。
st 不为 ed 儿子节点时老鼠可能向上走一段再走儿子。
二分 mid,如果花费<=mid 老鼠肯定不会走,否则需要堵住(x,y)。
老鼠在向儿子走之前每一步管理员会积累一次操作,如果当前需要堵的儿子数>操作数也是不合法的。

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
const int MAXN=1e6+5;
int n,st,ed,fa[MAXN],cnt,head[MAXN],dp[MAXN],f[MAXN],res,son[MAXN];
bool vis[MAXN];
struct ren{
	int nxt,to;
}a[MAXN<<1];
void add(int x,int y){
	a[++cnt].to=y;a[cnt].nxt=head[x];head[x]=cnt;
}
void dfs(int now,int F){
	fa[now]=F;
	for(int i=head[now];i;i=a[i].nxt){
		int u=a[i].to;if(u==F) continue;dfs(u,now);
		son[now]++;
	}
}
void dfs1(int now,int F){
	if(F) f[now]=f[F]+son[now]-1+(now==st);
	int ma1=0,ma2=0;
	for(int i=head[now];i;i=a[i].nxt){
		int u=a[i].to;if(u==F) continue;dfs1(u,now);if(dp[u]>ma1) ma2=ma1,ma1=dp[u];else if(dp[u]>ma2)  ma2=dp[u];
	}
	dp[now]=ma2+son[now];
}
bool check(int x){
	int cz=1;
	for(int now=st;now!=ed;cz++,now=fa[now]){
		int tt=0;
		for(int i=head[now];i;i=a[i].nxt){
			int u=a[i].to;
			if(vis[u]||dp[u]+f[now]<=x) continue;
			tt++;cz--;
		}
		if(cz<0) return false;
		x-=tt;
	}
	return x>=0;
}
int main(){
	scanf("%d%d%d",&n,&ed,&st);
	for(int i=1,x,y;i<n;i++){
		scanf("%d%d",&x,&y);add(x,y);add(y,x);
	}	
	dfs(ed,0);
	dfs1(ed,0);
	int op=st;
	while(op){vis[op]=1;op=fa[op];}
	int L=0,R=n-1;
	while(L<=R){
		int mid=(L+R)>>1;
		if(check(mid)) res=mid,R=mid-1;
		else L=mid+1;
	}
	printf("%d",res);return 0;
} 

标签:son,int,ed,CEOI2017,st,now,Mousetrap,dp
From: https://www.cnblogs.com/StranGePants/p/17709571.html

相关文章

  • CEOI2017 Building Bridges
    小清新斜率优化题。分段问题显然dp,令\(f_i\)为将第\(1\)根柱子和第\(i\)根柱子连接的最小代价。\(f_1=0\),每次枚举\(i\)向前直接连接的柱子:\[f_{i}=\min\limits_{j=1}^{i-1}\left\{f_j+(h_i-h_j)^2+\sum\limits_{k=j+1}^{i-1}w_k\right\}\]令\(s_{i}=\sum\limits_{j=......
  • [CEOI2017] Sure Bet(双指针)
    题目大意:给出两个数组A,B,可以在两个数组选择任意多个数,代价为选择的数的数目,得到的奖励为在数组A和数组B中选择的数的两个总和较小的那个,求能得到的最大收益思路:1.先给两个数组分别由大到小排序后求前缀和,不难得出在数组A中选择i个数,数组B中选择j个数时,最大收益为:m......
  • [CEOI2017] Mousetrap
    100黑祭。首先以终点为根。先考虑简单一点的情况:如果起点终点相邻,那么方案一定是让老鼠先走到一个叶子节点,然后断掉该节点到根路径上其它的分支。于是我们令\(f_i\)表示从\(i\)开始走到\(i\)子树里的一个叶节点再返回所需的最小代价,每次dp从儿子里的次大值转移即可。考虑......
  • P4654 [CEOI2017] Mousetrap
    \(\mathcalLink\)为了方便,以目标为根,向深度浅的位置走为“向上走”,否则为“向下走”。考虑到老鼠一旦开始向下走,它就一定会一直向下,直到走到叶子或者唯一向下的路被堵住......