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

    比较有趣的一道题,AGC做多了感觉想这种题的能力也变强了吧

    首先我们要看对题目:题目要求的是最小化管理员操作的次数,而并非两者博弈进行的轮次

    容易想到我们可以把陷阱设为整棵树的根,容易发现对于起点到根的路径最后老鼠是一定要走的,因此对于老鼠来说最优的决策一定是要向下走

    然后我们发现,对于管理员来说,完全可以先保持旁观,因为老鼠向下走迟早会把自己卡在一个叶节点,此时管理员只需要把老鼠到根节点的路径上的其它分支都堵住,然后把老鼠到根的路径上弄脏的边都擦掉即可

    考虑求出两个东西,(up_x)表示把(x)到根的路径上的所有分支都堵住的操作次数;(f_x)表示把老鼠卡死在(x)的操作次数(老鼠能走的边要么被堵住了要么被弄脏了)

    (up_x)的转移比较简单,(up_x=up_{fa_x}+deg_x-2+[x=st]),其中(st)表示起点,因为此时管理员还需要擦掉到(x)的一条边

    考虑(f_x)的转移,站在老鼠的角度,假设(yin son_x),那么它肯定会选择一个(max f_y)(y)钻进去,由于这里是管理员先操作,因此老鼠只有(operatorname{secondmax} f_y)(y)可以钻

    得到(f_x)的转移,(f_x=operatorname{secondmax}_{yin son_x} f_y+deg_x-1)

    然后刚开始naive地以为老鼠开始只能向下走,后来WA了好久才发现老鼠可以先向上走几步然后再钻进某棵子树

    刚开始想通过枚举老鼠从哪个点钻进子树,结果发现在老鼠向上的过程中管理员的操作就完全不可知,非常难处理

    遂转换思路,我们发现答案显然具有可二分性,于是我们把原问题转化成判定性问题

    考虑此时管理员剩下的操作步数(t),老鼠当前在点(x),枚举(yin son_x),此时若(up_x+f_y>t)老鼠肯定会选择一头钻进去

    我们再维护一个变量(left)表示管理员在老鼠向上跳的过程中腾出的操作数,如果(up_x+f_y>t)(left=0)那么就是老鼠的胜利,否则管理员可以通过(left-=1)来提前堵住这条边

    综上,这道题就解决了,复杂度(O(nlog n))

    #include<cstdio>
    #include<iostream>
    #include<cctype>
    #define RI register int
    #define CI const int&
    #define Tp template <typename T>
    using namespace std;
    const int N=1000005;
    struct edge
    {
    	int to,nxt;
    }e[N<<1]; int n,head[N],x,y,rt,st,cnt,ans,anc[N],f[N],up[N],deg[N]; bool tag[N];
    class FileInputOutput
    {
    	private:
    		static const int S=1<<21;
    		#define tc() ((A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin)),A==B)?EOF:*A++)
    		char Fin[S],*A,*B;
    	public:
    		Tp inline void read(T& x)
    		{
    			x=0; char ch; int flag=1; while (!isdigit(ch=tc())) if (ch=='-') flag=-1;
    			while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc())); x*=flag;
    		}
    		#undef tc 
    }F;
    inline void addedge(CI x,CI y)
    {
    	e[++cnt]=(edge){y,head[x]}; head[x]=cnt; ++deg[x];
    	e[++cnt]=(edge){x,head[y]}; head[y]=cnt; ++deg[y];
    }
    #define to e[i].to
    inline void DFS(CI now,CI fa=0)
    {
    	int mx=0,smx=0; anc[now]=fa;
    	if (now!=rt) up[now]=up[fa]+deg[now]-2+(now==st);
    	for (RI i=head[now];i;i=e[i].nxt) if (to!=fa)
    	{
    		DFS(to,now); if (f[to]>mx) smx=mx,mx=f[to];
    		else if (f[to]>smx) smx=f[to];
    	}
    	f[now]=smx+deg[now]-1;
    }
    inline bool check(int t)
    {
    	int left=1; for (RI now=st,i;now!=rt;now=anc[now],++left)
    	{
    		int dlt=0; for (i=head[now];i;i=e[i].nxt)
    		if (to!=anc[now]&&!tag[to]&&up[now]+f[to]>t)
    		if (!left) return 0; else ++dlt,--left; t-=dlt;
    	}
    	return t>=0;
    }
    #undef to 
    int main()
    {
    	RI i; F.read(n); F.read(rt); F.read(st); if (st==rt) return puts("0"),0;
    	for (i=1;i<n;++i) F.read(x),F.read(y),addedge(x,y);
    	for (DFS(rt),i=st;i;i=anc[i]) tag[i]=1;
    	int l=0,r=n-1,mid; while (l<=r)
    	if (check(mid=l+r>>1)) ans=mid,r=mid-1; else l=mid+1;
    	return printf("%d",ans),0;
    }
    
  • 相关阅读:
    SpringBoot 调用 K8s metrics-server
    Kubernetes 实战——有状态应用(StatefulSet)
    Kubernetes 实战——升级应用(Deployment)
    Kubernetes 实战——发现应用(Service)
    Kubernetes 实战——配置应用(ConfigMap、Secret)
    Java 集合使用不当,Code Review 被 diss了!
    30 个 ElasticSearch 调优知识点,都给你整理好了!
    关于线程池的面试题
    Java面试必问,ThreadLocal终极篇
    Java代码中,如何监控Mysql的binlog?
  • 原文地址:https://www.cnblogs.com/cjjsb/p/14077540.html
Copyright © 2011-2022 走看看