JuRuoOIer
2024-11-19 22:50:12
前排提示:本题为结论题。本篇题解旨在阐明本人是怎么想到的,并给出一个(自认为)比较易懂的证明。
本文中称 hgcnxn 为“抓的”,Drifty 为“跑的”。
另:尽管写得不少,思维可能还是不太连贯,如果有我没讲明白的地方,欢迎在评论区或者私信问我~
看上去题意很绕,各种知道不知道的。但比较通俗地理解一下,其实就是抓的只要无法同时顾及跑的可能出现所有的位置,跑的就一定不会被抓住。
这种题的特殊性质通常是能够引领思路的。因此我们先来考虑这些性质。
由于抓的知道跑的的初始位置,所以抓的只要往那个位置走就能把跑的逼死,此时抓的必胜。
仍然是由于抓的知道跑的的初始位置,因此抓的开局上根节点(如果已经在了直接到下一步)再往跑的的所在方向走,跑的就跑不掉。
这时情况多了起来。不要慌,耐心地进行分讨就好。
先不妨设每条链都很长但长度有限。这里称靠近“花蕊”的方向为“上”,反方向为“下”。
综合上述情况,跑的能够胜利,当且仅当跑的可能先抓的一步(不含)以上到达花蕊。
但是我们这里的前提是每条链足够长,现在考虑链很短的情况。
综上,性质 C 中“花蕊”所连的三条链都必须“很长”,跑的才能赢。现在我们需要“很长”的定义。
刚才我们说,长度为
我们发现,此时当抓的下到链底时,跑的可以趁此机会换到另一条链。也就是说,刚才的“很长”其实就是长度大于等于
到这里,我们自然而然地猜想到:
如果图中存在一个和上图结构相同的子图,且跑的能先于抓的到达该子图的“花蕊”处,则会出现上图中的情形,跑的必胜;否则抓的必胜。
然后我们发现,证明其充分性就是上图,证必要性就是我们刚才证明某条链长度为
枚举每个点及其每条出边,如果存在至少三条出边,满足该边另一端的点度数大于等于
距离可以通过遍历进行预处理,每条边最多只会被枚举两次,所以时间复杂度
ll t,n,p,q,u,v,lp[200010],lq[200010];
//lp[i] 和 lq[i] 分别表示从 p 和从 q 到 i 的最短距离
vector<ll> a[200010];
void dfs(ll now,ll dep,ll lst,ll op){//遍历求出 lp 和 lq 数组
if(op)lq[now]=dep;
else lp[now]=dep;
for(int i=0;i<a[now].size();i++){
if(a[now][i]!=lst){
dfs(a[now][i],dep+1,now,op);
}
}
}
bool check(ll now){//判断是否为满足要求的点
ll tot=0;
for(int i=0;i<a[now].size();i++){
if(a[a[now][i]].size()>1)tot++;
if(tot>2)return true;
}
return false;
}
int main(){
ios::sync_with_stdio(0);
cin>>t;
while(t--){
cin>>n>>p>>q;
for(int i=1;i<=n;i++){
a[i].clear();//记得清空
}
for(int i=1;i<n;i++){
cin>>u>>v;
a[u].push_back(v);
a[v].push_back(u);
}
dfs(p,0,-1,0);//预处理距离
dfs(q,0,-1,1);
bool flg=0;
for(int i=1;i<=n;i++){
if(check(i)&&lp[i]>lq[i]+1){//存在一个符合条件的点跑的就必胜
flg=1;
break;
}
}
if(flg)cout<<"Drifty\n";
else cout<<"hgcnxn\n";
}
return 0;
}