[CEOI2017]Mousetrap

P4654 [CEOI2017]Mousetrap

博弈论既视感

身临其境感受耗子和管理的心理历程。

以陷阱为根考虑。就要把耗子赶到根部。

首先一定有解。

作为耗子,为了拖延时间,必然会找到一个子树往下走。

如果耗子从子树往下走的话,

那么一定会走到一个叶子被自己卡住。

作为管理,堵路比清理容易的多。否则耗子进去了还得清理把它轰出来。

所以,耗子进入一个子树,管理先手,必定堵住花费最大的子树,耗子会到次大的子树

然后耗子还要赶回来,赶回来之前,一定先把沿途其他的路堵住,再放耗子回来

所以树形dp

in[x]表示,耗子从以x为根的子树往下走,再回到x共花费的次数

in[x]=cmx(in[y])+du[x]-2+1

cmx表示次大值。度数这一层要堵住的其他路,+1是走下去的这个边还要清理。

 

耗子回来了,怎么办?

耗子往上走,找机会还会往下。所以综合考虑,不如在耗子困进子树叶子里之后,先把到root的链上所有其他分支堵住,然后再放耗子出来,一定就进了陷阱

cos[x]=in[x]+沿途度数和

 

但是耗子不一定往下走。可能一开始往上走,再往下走

不好处理,不如二分

沿着链往上走,记录还能行动的次数,以及可以多k次先手(先手有用,省着先不做)

如果一个点管理员瞎玩去了没有干啥,耗子进入最大子树,这样花费已经固定。

考虑花费是否大于剩余的步数

否则用一次先手机会,然后花费一次行动堵住。

如果行动不够,或者先手不够,那么GG

注意堵儿子的时候,之前堵上的就不用再堵了,所以记录tmp为堵住的叶子个数。

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=1e6+6;
const int inf=0x3f3f3f3f;
struct node{
    int nxt,to;
}e[2*N];
int hd[N],cnt;
int du[N];
int n,t,m;
int in[N],cos[N],dep[N];
int fa[N];
void add(int x,int y){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
}
bool cmp(int x,int y){
    return cos[x]>cos[y];
}
void dfs(int x,int d,int c){
    dep[x]=d;
    int cmx=0,mx=0;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa[x]) continue;
        fa[y]=x;
        if(x!=t) dfs(y,d+1,c+du[x]-2);
        else dfs(y,d+1,c);
        if(in[y]>mx){
            cmx=mx;mx=in[y];
        }else{
            cmx=max(cmx,in[y]);
        }
    }
    in[x]=cmx+1+du[x]-2;
    cos[x]=in[x]+c+(fa[x]==m);
}
bool che(int mid){//root is t ; start is m
    //cout<<" mid "<<mid<<endl;
    int re=mid;
    int k=0;
    int now=m;
//    if(ch[now].size()){
//        if(cos[ch[now][0]]>=re) --re;
//        else ++k;
//    }
//    if(re<0) return false;
//    if(k<0) return false;
    //cout<<" midmid "<<mid<<" : "<<re<<endl;
    int las=0;
    //now=fa[now];
    while(now!=t){
        ++k;
        int tmp=0;
        for(reg i=hd[now];i;i=e[i].nxt){
            int y=e[i].to;
            if(y==las||y==fa[now]) continue;
            if(cos[y]-tmp>re){
                --k;--re;
                ++tmp;
            }
            if(re<0) return false;
            if(k<0) return false;
        }
        las=now;
        now=fa[now];
    }
    return true;
}
int main(){
    rd(n);rd(t);rd(m);
    if(m==t){
        puts("0");return 0;
    }
    int x,y;
    for(reg i=1;i<n;++i){
        rd(x);rd(y);
        ++du[x];++du[y];
        add(x,y);add(y,x);
    }
    dfs(t,1,0);
//    for(reg i=1;i<=n;++i){
//        cout<<" ii "<<i<<" : "<<in[i]<<" "<<cos[i]<<endl;
//    }
    int L=0,R=n+233;    
    L=cos[m];
    int ans=0;
    while(L<=R){
        int mid=(L+R)>>1;
        if(che(mid)){
            ans=mid;
            R=mid-1;
        }else{
            L=mid+1;
        }
    }    
    printf("%d",ans);
    return 0;
}

}
signed main(){    
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2018/12/29 10:05:21
*/



/*
   Author: *Miracle*
   Date: 2018/12/29 10:05:21
*/

总结:

挺不错的小游戏

还是考虑每个角色的策略吧。

有一些分析和直觉猜测。

posted @ 2018-12-29 15:44  *Miracle*  阅读(422)  评论(0编辑  收藏  举报