効果の件名:
n個のノードのツリーが知られています。我々は(何LCA約任意の2つのノードたら(切り捨て)のn / 2 LCAを求めることができ https://www.geeksforgeeks.org/lowest-common-ancestor-binary-tree-set-1/、)私たちは、ルートノードを決定する方法を尋ねました。
N <= 1E3
問題解決のアイデア:
nはここの範囲なので、我々は、n ^ 2のアルゴリズムを検討します。
まず、結論あります:たびに私たちは、学位LCA 1(u、v)は、LCA = uまたはLCA = V場合、ルートは確かにLCAでの2つのノードが尋ねました。これは矛盾で証明することができます。Uの場合は、V uは、VのLCAをu以上、それを下げるにあるVを1つのみの表しているので、この結論は、保持していない、そして程度、1されていません。
2つの点が1で要求されているLCAに来れば毎回ので、我々は、任意の2つのリーフノード(度ポイント1のすなわちリーフノード)を列挙し、その後、我々は終了します。そうでない場合、我々は2枚の葉が繰り返しに聞いていき削除することができます。これは、完全な質問とルートノードにN / 2倍を可能にします。
学位論文は時々良い出発点であるとき、ここではこの概念の使用の程度は、問題の類似のトポロジカルソートがあり、計画を立てます。
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;cin>>n;
int deg[n];
memset(deg,0,sizeof(deg));
vector<vector<int>> gra(n);
set<int> ms;
for(int i=0;i<n-1;i++){
int u,v;cin>>u>>v;
u--;v--;
deg[u]++;
deg[v]++;
gra[u].push_back(v);
gra[v].push_back(u);
ms.insert(i);
}
ms.insert(n-1);
int l,r;
l=r=-1;
while(1){
l=-1;r=-1;
for(int i=0;i<n;i++){
if(deg[i]==1){
if(l==-1)l=i;
else r=i;
}
}
if(l==-1 || r==-1)break;
printf("? %d %d\n",l+1,r+1);
fflush(stdout);
// cerr<<l<<" "<<r<<endl;
int w;cin>>w;
w--;
if(w==l || w==r){
printf("! %d\n",w+1);
fflush(stdout);
return 0;
}
deg[l]--;
for(int i=0;i<(int)gra[l].size();i++){
int nx=gra[l][i];
deg[nx]--;
}
deg[r]--;
for(int i=0;i<(int)gra[r].size();i++){
int nx=gra[r][i];
deg[nx]--;
}
ms.erase(l);
ms.erase(r);
}
assert(ms.size());
printf("! %d\n",*ms.begin()+1);
fflush(stdout);
return 0;
}