[CF1854D] Michael and Hotel

题目描述

Michael and Brian are stuck in a hotel with $ n $ rooms, numbered from $ 1 $ to $ n $ , and need to find each other. But this hotel's doors are all locked and the only way of getting around is by using the teleporters in each room. Room $ i $ has a teleporter that will take you to room $ a_i $ (it might be that $ a_i = i $ ). But they don't know the values of $ a_1,a_2, \dots, a_n $ .

Instead, they can call up the front desk to ask queries. In one query, they give a room $ u $ , a positive integer $ k $ , and a set of rooms $ S $ . The hotel concierge answers whether a person starting in room $ u $ , and using the teleporters $ k $ times, ends up in a room in $ S $ .

Brian is in room $ 1 $ . Michael wants to know the set $ A $ of rooms so that if he starts in one of those rooms they can use the teleporters to meet up. He can ask at most $ 2000 $ queries.

The values $ a_1, a_2, \dots, a_n $ are fixed before the start of the interaction and do not depend on your queries. In other words, the interactor is not adaptive.

$ 2 \leq n \leq 500 $

当你已经知道了 \(1\) 所在的环之后,就很好判断一个点和在一个连通块了。判断一下这个点走 \(n\) 步之后是否在那个环就行了。

我们可以用二分求出一个 \(a_x\).

可以先用 \(1\)\(n\) 步得到环上的某一个点 \(x\),如何扩展出整个环。首先可以一个个求 \(a_x\),那么大概是 \(9n\) 的时间,不行。

考虑倍增。假设我们现在得到了环上的 \(c\) 个点,那么我们可以通过询问一个点走 \(c\) 步是否在环上,这样可以在 \(O(n)\) 的次数内求出 \(c\) 个环上的点。

考虑把上面两种方式结合起来,先用第一种方法求出 \(64\) 个点,然后用第二种方法倍增出剩下的就行了。

细节很多。

#include<bits/stdc++.h>
using namespace std;
const int N=505;
int n,x,a[N],v[N],p[31][N],k,q[N],fa[N],m=1,cnt=0;
mt19937 gen(time(0));
int find(int x)
{
	if(fa[x]==x)
		return x;
	return fa[x]=find(fa[x]);
}
int ok()
{
	int c=0;
	for(int i=1;i<=n;i++)
		fa[i]=i;
	for(int i=1;i<=n;i++)
		fa[find(p[0][i])]=find(i);
	for(int i=1;i<=n;i++)
		if(find(i)==find(1))
			++c;
	if(c^m)
		return 0;
	for(int i=1;i<=m;i++)
		if(find(a[i])^find(1))
			return 0;
	return 1;
}
void maker()
{
	for(int i=1;i<=n;i++)
	{
		p[0][i]=gen()%n+1;
		//printf("%d ",p[0][i]);
	}
	for(int j=1;j<=30;j++)
		for(int k=1;k<=n;k++)
			p[j][k]=p[j-1][p[j-1][k]];
}
/*int qry(int x,int k,int m)
{
	++cnt;
	int px=x;
	if(x>n||x<1)
	{
		puts("Wrong query");
		return 0;
	}
	for(int i=30;~i;--i)
		if(k>>i&1)
			x=p[i][x];
	for(int i=1;i<=m;i++)
		if(q[i]==x)
			return 1;
	return 0;
}*/
int ask(int u,int k)
{
	int l=1,r=n;
	while(l<r)
	{
		int md=l+r>>1;
		printf("? %d %d %d ",u,k,md-l+1);
		for(int i=l;i<=md;i++)
			printf("%d ",i);
		puts("");
		fflush(stdout);
		scanf("%d",&x);
		/*for(int i=l;i<=md;i++)
			q[i-l+1]=i;
		x=qry(u,k,md-l+1);*/
		if(x)
			r=md;
		else
			l=md+1;
	}
	return l;
}
int main()
{
	scanf("%d",&n);
	maker();
	a[1]=ask(1,1000000000);
	v[a[1]]=1;
	for(int i=1;i>=0&&i<=n;i<<=1)
	{
		int pm=m;
		if(i*9<n)
		{
			for(int j=1;j<=i;j++)
			{
				a[m+1]=ask(a[m],1),++m;
				if(v[a[m]])
					j=i,i=-1,--m;
				else
					v[a[m]]=1;
			}
		}
		else
		{
			for(int j=1;j<=n;j++)
			{
				if(v[j])
					continue;
				printf("? %d %d %d ",j,i,m);
				for(int k=1;k<=m;k++)
					printf("%d ",a[k]);
				puts("");
				fflush(stdout);
				scanf("%d",&x);
				/*for(int k=1;k<=i;k++)
					q[k]=a[k];
				x=qry(j,i,i);*/
				if(x)
					v[a[++m]=j]=1;
			}
		}
		if(m==pm)
			break;
		if(2*i>m)
		break;
	}
	q[1]=a[1];
	for(int i=1;i<=n;i++)
	{
		if(!v[i])
		{
			printf("? %d 1000000000 %d ",i,m);
			for(int j=1;j<=m;j++)
				printf("%d ",a[j]);
			puts("");
			fflush(stdout);
			scanf("%d",&x);
			/*for(int j=1;j<=m;j++)
				q[j]=a[j];
			x=qry(i,1000000000,m);*/
			if(x)
				a[++m]=i;
		}
	}
	printf("! %d ",m);
	for(int i=1;i<=m;i++)
		printf("%d ",a[i]);
	fflush(stdout);
	/*if(ok())
		printf("succes,%d\n",cnt);
	else
		puts("failed");*/
	return 0;
}

posted @ 2023-09-04 15:31  灰鲭鲨  阅读(6)  评论(0编辑  收藏  举报