【CF1370F2】The Hidden Pair (Hard Version)

题目

题目链接:https://codeforces.com/problemset/problem/1370/F2
这是一道交互题。困难版与简单版唯一的区别是交互次数限制。

本题有多组数据。

已知一棵 \(n\) 个顶点的树(边集已知),其中有两个不同的顶点被暗中做了标记。你现在需要通过若干次询问,猜出两个被标记顶点的编号。

一次询问的格式为 ? c x_1 x_2 ... x_c,即代表你向交互库请求关于 \(x_1,x_2,\cdots,x_c\)\(c\) 个点的信息。

对于一次询问,交互库的返回格式为 x d,表示在询问的集合中,到两个被标记点的距离之和最小的点是 \(x\),这个最小值为 \(d\)。如果有多个最小值点,\(x\) 的值可能是其中任意一个。

如果已经知晓答案,请用 ! x y 的格式来输出你的答案,任意顺序均可。在这之后,你会收到一个字符串 Correct 或者 Incorrect,代表你的猜测是否正确。如果收到了 Incorrect,请立即终止程序,否则请继续处理下一组数据。

对于每组数据,请你在不超过 \(11\) 次询问之内给出答案。(Easy Version 是 \(14\) 次)

\(1\le t\le 10, 2\le n\le 1000\)

思路

首先询问一遍所的点,可以得到两个标记点的距离 \(dis\),以及位于它们路径上的一个点。
然后以得到的这个点开始 dfs,用 vector 记录每一个点的深度。
然后二分深度,每次询问这个深度上的所有点。如果得到的距离是 \(dis\),说明较深的那一个点一定深度不小于当前二分的深度。
可以在 \(O(\log n)\leq 10\) 次询问找到其中一个标记点。以这个标记点为根 dfs,然后询问所有深度为 \(dis\) 的点,得到的就是另一个标记点了。
操作次数为 \(1+10+1=12\),可以过 Easy Version。
我们发现,因为第一次二分的是较深的点,它的深度一定不小于 \(\lceil\frac{dis}{2}\rceil\),这样我们二分的区间就变成了 \([\lceil\frac{dis}{2}\rceil,\min(dis,maxdep)]\)。显然这个的上界是 \(\frac{n}{2}\leq 500\),那么此时的操作次数为 \(1+\log \frac{n}{2}+1\geq 1+9+1=11\)。可以通过 Hard Version。
时间复杂度 \(O(Qn\log n)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=1010;
int n,Q,rt,dis,res,maxd,tot,head[N];
char ch[20];
vector<int> node[N];

struct edge
{
	int next,to;
}e[N*2];

void add(int from,int to)
{
	e[++tot]=(edge){head[from],to};
	head[from]=tot;
}

void prework()
{
	for (int i=0;i<=maxd;i++) node[i].clear();
	memset(head,-1,sizeof(head));
	tot=maxd=0;
}

void dfs(int x,int fa,int dep)
{
	maxd=max(maxd,dep);
	node[dep].push_back(x);
	for (int i=head[x];~i;i=e[i].next)
		if (e[i].to!=fa) dfs(e[i].to,x,dep+1);
}

int main()
{
	scanf("%d",&Q);
	while (Q--)
	{
		prework();
		scanf("%d",&n);
		for (int i=1,x,y;i<n;i++)
		{
			scanf("%d%d",&x,&y);
			add(x,y); add(y,x);
		}
		
		// -----------------------------------------------------------
		printf("? %d",n);
		for (int i=1;i<=n;i++)
			printf(" %d",i);
		printf("\n");
		fflush(stdout);
		// -----------------------------------------------------------
		
		scanf("%d%d",&rt,&dis);
		dfs(rt,0,0);
		int l=dis/2,r=min(maxd,dis),mid,x,y;
		while (l<=r)
		{
			mid=(l+r)>>1;
			
			// -----------------------------------------------------------
			printf("? %d",node[mid].size());
			for (int i=0;i<node[mid].size();i++)
				printf(" %d",node[mid][i]);
			printf("\n");
			fflush(stdout);
			// -----------------------------------------------------------
			
			scanf("%d%d",&x,&y);
			if (y==dis) l=mid+1,res=x;
				else r=mid-1;
		}
		for (int i=0;i<=maxd;i++) node[i].clear();
		dfs(res,0,0);
		
		// -----------------------------------------------------------
		printf("? %d",node[dis].size());
		for (int i=0;i<node[dis].size();i++)
			printf(" %d",node[dis][i]);
		printf("\n");
		fflush(stdout);
		// -----------------------------------------------------------
		
		scanf("%d%d",&x,&y);
		printf("! %d %d\n",x,res);
		fflush(stdout);
		scanf("%s",ch);
	}
	return 0;
}
posted @ 2020-12-30 13:32  stoorz  阅读(70)  评论(0编辑  收藏  举报