博弈论既视感
身临其境感受耗子和管理的心理历程。
以陷阱为根考虑。就要把耗子赶到根部。
首先一定有解。
作为耗子,为了拖延时间,必然会找到一个子树往下走。
如果耗子从子树往下走的话,
那么一定会走到一个叶子被自己卡住。
作为管理,堵路比清理容易的多。否则耗子进去了还得清理把它轰出来。
所以,耗子进入一个子树,管理先手,必定堵住花费最大的子树,耗子会到次大的子树
然后耗子还要赶回来,赶回来之前,一定先把沿途其他的路堵住,再放耗子回来
所以树形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 */
总结:
挺不错的小游戏
还是考虑每个角色的策略吧。
有一些分析和直觉猜测。