CodeForces - 1305 D - Kuroni and the Celebration(交互题,思维)

传送门

题意:

n个点,n-1条边,通过每次向系统询问两个点,它会告诉这两个点的lca
最多询问 n 2 \lfloor \frac{n}{2}\rfloor
找到根结点并输出

思路:

考虑一颗树的两个叶子结点,如果他们的lca是其中的一个叶子结点,那么lca一定是根结点,如果他们的lca不是这两个叶子结点,那么这两个点肯定都不可能是根结点。
具体做法:就是把每次向系统询问两个叶子结点,如果他们的lca是其中的一个叶子结点,那直接结束。如果没找到,那把这两个点删了,然后和这两个点相连的点的度都减1,减1之后如果它等于1(即变成了叶子结点),把它仍到队列里,继续向系统询问
如果n为偶数,最多n/2次询问,肯定能找到
如果n为奇数,最多 n 2 \lfloor \frac{n}{2}\rfloor 次询问,要么提前找到,要么根结点在队列里,因为询问 n 2 \lfloor \frac{n}{2}\rfloor 次,队列里还剩下一个元素,就是根结点

代码:

#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;
}


发布了158 篇原创文章 · 获赞 15 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44091178/article/details/104653597