zoukankan      html  css  js  c++  java
  • P4654 [CEOI2017]Mousetrap 题解

    Link

    P4654 [CEOI2017]Mousetrap

    Solve

    这道题是一个树上博弈问题,如果同时从管理者和老鼠两个角度考虑会比较复杂,所以先从老鼠的角度来看

    方便起见,把陷阱的节点看做根节点,老鼠就可以看错被往上赶。

    对于每个节点构成的子树,我们定义(F[i])表示老鼠进到这个子树,又被赶回来的最少步数(从管理者的角度来说)

    接下来就是一个树形(DP)了,对于老鼠,进到一个树里面,假设我已经知道每个子节点的(F[i]),老鼠肯定往(F[i])大的子节点跑(让管理员封更多次呗),由于是管理员先手,他能预判到老鼠会往(F[i])最大的子节点里面跑,所以就提前把(F[i])最大的子节点堵死,但是老鼠预判了管理员的预判,钻进(F[i])的次大值的节点,由于老鼠进入一个节点就被卡死了,只能等管理员来疏通,所以对于管理员,当老鼠进入次大子节点后,把(i)这个节点的其他边全部封死,防止老鼠又钻到别的子树里面(到父亲的节点不用封死,以为已经脏了)(对于次大值,要加1的代价把路疏通,好让老鼠从次大值的节点回来)

    对于没有看懂的可以看一下图

    DoTwWQ.png

    所以就推出了转移方程,(Max_2)表示子节点(F[])的次大值,(du[i])表示节点的度数

    [F[i]=Max_2+du[i]-1 ]

    对于老鼠来说,肯定在往向根节点的路上钻进某个子树里面,让管理员的代价最大,注意,最多只能钻进一个子树,以为对于管理员来说,可以等老鼠被卡死的时候把改堵的全堵了,然后再疏通,让老鼠只能往根节点走。

    如果老鼠能知道最后的答案就能知道到底要不要钻了,管理员也能预判到要不要堵了。

    考虑答案有单调性,就用二分来处理。

    二分最终的答案(k),构造出一条从(m)(t)的路径,枚举路径上碰到的每个子节点如果老鼠钻进去能让管理员在(k)的时间内完不成,肯定要钻,管理员为了不让老鼠钻进这些子节点,要提前把这些子节点堵上。

    我是这么判断的,(此时在从(m)(t)的链上处理)(sum[i])表示,老鼠钻进去后腰把后面七七八八的别的边都堵了

    [if(sum[i]+F[son[j]]+1-(i!=1)>k)nowcnt++ ]

    这里((i!=1))是因为当(g[i]==m)时计算(sum[i]),被多减了(1),现在要加回去

    然后把(k-=nowcnt),把多出来要堵的步骤减去

    如果在统计过程中,(k<0)了,能用的步骤已经用完了,还没堵死,只能(return 0)

    还有一种情况,就是(i<allcnt)((allcnt)是把之前的(nowcnt)加起来,表示一共要堵的步骤)由于管理员堵一次,老鼠就走一下,如果管理员不能把该堵的都堵了,也就是不能控制老鼠到规定的节点,老鼠就会乱跑,管理员就控制不住了,也肯定不是最优解,所以(return 0)

    除了这两种情况(return 1)就行了

    这道题细节较多

    Code

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1000005,maxe=2000005;
    int F[maxn],sum[maxn],fa[maxn],T,M,N,du[maxn],g[maxn],tot,ans;
    int cnt,lnk[maxn],son[maxe],nxt[maxe];
    inline int read(){
    	int ret=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
    	while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
    	return ret*f;
    }
    inline void add_e(int x,int y){son[++cnt]=y;nxt[cnt]=lnk[x];lnk[x]=cnt;}
    void DFS(int x,int f){
    	int Max_1=0,Max_2=0;
    	for(int j=lnk[x];j;j=nxt[j]){
    		if(son[j]==f)continue;
    		fa[son[j]]=x;DFS(son[j],x);
    		if(F[son[j]]>=Max_1){Max_2=Max_1;Max_1=F[son[j]];}
    		else if(F[son[j]]>Max_2)Max_2=F[son[j]];
    	}
    	F[x]=Max_2+du[x]-1;
    	return ;
    }
    inline bool check(int k){
    	int all_cnt=0;
    	for(int i=1;i<=tot;i++){
    		int now_cnt=0;
    		for(int j=lnk[g[i]];j;j=nxt[j]){
    			if(son[j]==g[i+1]||son[j]==g[i-1])continue;
    			if(sum[i]+F[son[j]]+1-(i!=1)>k)now_cnt++;
    		}
    		all_cnt+=now_cnt;k-=now_cnt;
    		if(k<0||all_cnt>i)return 0;
    	}
    	return 1;
    }
    int main(){
    	freopen("mouse.in","r",stdin);
    	freopen("mouse.out","w",stdout);
    	N=read();T=read(),M=read();
    	for(int i=1;i<N;i++){
    		int x=read(),y=read();
    		add_e(x,y);add_e(y,x);
    		du[x]++;du[y]++;
    	}
    	DFS(T,0);
    	for(int i=M;i;i=fa[i])g[++tot]=i;
    	for(int i=tot-1;i;i--)sum[i]=sum[i+1]+du[g[i]]-2;
    	int L=0,R=1e9;
    	while(L<=R){
    		int mid=(R-L>>1)+L;
    		if(check(mid))R=mid-1,ans=mid;
    		else L=mid+1;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    采购到入库所经历的表
    PO 收料SQL
    关于PO 和PR 的联系问题
    在Oracle Form中,如何实现自动编号(行号)的功能
    订单暂挂问题sql解决:
    类和结构的区别?
    DataTable.Select 方法 (String, String, DataViewRowState)
    Ref与Out的区别
    C# 反射
    委托
  • 原文地址:https://www.cnblogs.com/martian148/p/14077760.html
Copyright © 2011-2022 走看看