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

    Link
    我们以有陷阱的房子为根。
    一旦老鼠向下进入一个子树内,且管理员不帮它清理上一次走过的道路,那么它最终会被自己弄脏的走廊困在某个叶子节点。
    老鼠被困在叶子节点之后管理员的最优决策一定是先把这个叶子节点到根节点路径上的岔路口全部堵上,然后一次次把老鼠当前位置到父亲的边清理直到老鼠走到根。
    因此在老鼠向下走时管理员帮它清理上一次走过的道路一定是不优的。管理员本可以在这期间进行堵上岔路口的操作。
    那么现在情况就是老鼠先往上走一段距离,然后走进一棵子树。
    走进任意一棵子树之后的操作步数是可以dp出来的,设(f_u)表示老鼠进入了(u)的子树,然后又被赶回(u)的最小步数。
    在老鼠向下走之后管理员每次堵住当前点通向(f)值最大的子树的边一定最优。
    因此转移方程为(f_u=operatorname{2ndmax}limits_{vin son_u}(f_v)+deg_u-1)
    然后我们再处理出(sum_u)表示(u)节点到根路径上的岔路口数。
    那么老鼠第一次向下走,走到(u),然后游戏结束的最少步数为(g_u=f_u+sum_{fa_u}-[fa_u e start])
    那么现在问题就是老鼠第一次向下走会走到哪个点。
    二分答案(lim)变成判定性问答题,老鼠肯定是想找个(g_u>lim)的点走下去,而管理员就需要把这样的点封上。因为管理员在操作(lim)也会变小。
    最后如果能够全部堵上那么(lim)时间就是合法的,否则不合法。

    #include<cctype>
    #include<cstdio>
    #include<vector>
    #include<algorithm>
    const int N=1000007;
    char ibuf[1<<24],*iS=ibuf;int n,root,start,cnt,fa[N],f[N],id[N],sum[N];std::vector<int>e[N],g[N];
    int read(){int x=0;while(isspace(*iS))++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return x;}
    void dfs(int u)
    {
        int mx1=0,mx2=0;
        for(int v:e[u])
        {
    	if(v==fa[u]) continue;
    	fa[v]=u,dfs(v);
    	if(f[v]>mx1) mx2=mx1,mx1=f[v]; else if(f[v]>mx2) mx2=f[v];
        }
        f[u]=mx2+(int)e[u].size()-1;
    }
    int check(int lim)
    {
        for(int i=cnt,res=0;i>1;--i)
        {
    	++res;int now=0;
    	for(int v:g[id[i]])
    	    if(v-now>lim)
    	    {
    		if(!res||!lim)return 0;
    		--lim,--res,++now;
    	    }
        }
        return 1;
    }
    int find()
    {
        int l=0,r=n,mid;
        while(l<=r) mid=(l+r)/2,check(mid)? r=mid-1:l=mid+1;
        return l;
    }
    int main()
    {
        fread(ibuf,1,1<<24,stdin);
        n=read(),root=read(),start=read(),e[root].push_back(0);
        for(int i=1,u,v;i<n;++i) u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
        dfs(root);
        for(int u=start;u;u=fa[u]) id[++cnt]=u;
        std::reverse(id+1,id+cnt+1);
        for(int i=1;i<=cnt;++i) sum[i]=sum[i-1]+(int)e[id[i]].size()-2+(i==cnt);
        for(int i=cnt,u;u=id[i],i;--i) for(int v:e[u]) if(v!=id[i-1]&&v!=id[i+1]) g[u].push_back(f[v]+sum[i]);
        printf("%d",find());
    }
    
  • 相关阅读:
    [转]SVN服务器搭建和使用(二)
    [转]SVN服务器搭建和使用(一)
    BZOJ 2049 Sdoi2008 Cave 洞穴勘测
    BZOJ 1589 Usaco2008 Dec Trick or Treat on the Farm 采集糖果
    BZOJ 2796 POI2012 Fibonacci Representation
    BZOJ 2115 Wc2011 Xor
    BZOJ 3105 CQOI2013 新Nim游戏
    BZOJ 2460 Beijing2011 元素
    BZOJ 3687 简单题
    BZOJ 1068 SCOI2008 压缩
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/13024245.html
Copyright © 2011-2022 走看看