题意:
n个点,n-1条边,通过每次向系统询问两个点,它会告诉这两个点的lca
最多询问
次
找到根结点并输出
思路:
考虑一颗树的两个叶子结点,如果他们的lca是其中的一个叶子结点,那么lca一定是根结点,如果他们的lca不是这两个叶子结点,那么这两个点肯定都不可能是根结点。
具体做法:就是把每次向系统询问两个叶子结点,如果他们的lca是其中的一个叶子结点,那直接结束。如果没找到,那把这两个点删了,然后和这两个点相连的点的度都减1,减1之后如果它等于1(即变成了叶子结点),把它仍到队列里,继续向系统询问
如果n为偶数,最多n/2次询问,肯定能找到
如果n为奇数,最多
次询问,要么提前找到,要么根结点在队列里,因为询问
次,队列里还剩下一个元素,就是根结点
代码:
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <vector>
#include <math.h>
#include <map>
#include <queue>
#include <set>
#include <stack>
typedef long long ll;
#define PII make_pair
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int MAXN=2e5+50;
const int inf=0x3f3f3f3f;
const int M=5000*4;
vector<int>p[1050];
int d[1050];
int main()
{
int n;
scanf("%d",&n);
rep(i,1,n-1){
int u,v;
scanf("%d%d",&u,&v);
p[u].pb(v);
p[v].pb(u);
d[u]++,d[v]++;
}
queue<int>q;
rep(i,1,n)if(d[i]==1)q.push(i);
int ans=-1;
while(q.size()>1){
int u=q.front();q.pop();
int v=q.front();q.pop();
printf("? %d %d\n",u,v);
fflush(stdout);//用来清空输出缓存区的
int lca;
scanf("%d",&lca);
if(lca==u||lca==v){
ans=lca;
break;
}
for(int i=0;i<p[u].size();i++){
int s=p[u][i];
d[s]--;
if(d[s]==1)q.push(s);
}
for(int i=0;i<p[v].size();i++){
int s=p[v][i];
d[s]--;
if(d[s]==1)q.push(s);
}
}
if(ans==-1)ans=q.front();
printf("! %d\n",ans);
return 0;
}