【洛谷P7045】金牌

题目

题目链接:https://www.luogu.com.cn/problem/P7045
书虫正在整理他的 \(n\) 块金牌,他发现所有金牌都是有磁性的!形式地说,每块金牌属于一种磁极,磁极有很多种。两块相邻的金牌磁极相同则相互排斥,不同则相互吸引。
书虫不知道每块金牌的磁极,他只能通过把两块金牌靠近的方式得知它们是相同磁极还是不同磁极。换句话说,你可以进行不超过 \(Q\) 次交互,每次向交互库询问两个数 \(x,y\),交互库会返回第 \(x\) 块金牌和第 \(y\) 块金牌是排斥还是吸引。金牌从 \(0\)\(n-1\) 编号。
书虫希望把他的金牌排成一个排列,满足任意两块相邻的金牌都相吸引,请你帮他排出 任意一个 合法的排列,或者告诉他无解。

思路

QuantAsk YYDS!
有一个很直接的想法是我们枚举每一块金牌 \(i\),将前 \(i\) 块中剩余的金牌扔到队列里。如果第 \(i\) 块金牌和再队列里的金牌不一致,那么就将这两块金牌都加入答案序列,否则将 \(i\) 压入队列中。
那么最后有若干个多出来的互相排斥的金牌在队列中,我们就枚举答案序列,如果相邻两项均与在队列中的金牌吸引,那么就在它们之间插入一块金牌。
由于我们最开始将 \(0\) 直接加入到答案序列中,所以最多会有 \(2(n-1)=2n-2\) 次交互。

代码

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

const int N=100010;
int Q,n,m,ret,ans[N];
queue<int> q;

void prework()
{
	while (q.size()) q.pop();
	m=1; ans[1]=0;
}

int main()
{
	scanf("%d",&Q);
	while (Q--)
	{
		scanf("%d%d",&n,&m);
		prework();
		for (int i=1;i<n;i++)
			if (!q.size())
			{
				printf("%d %d\n",i,ans[m]);
				fflush(stdout);
				scanf("%d",&ret);
				if (ret) ans[++m]=i;
					else q.push(i);
			}
			else
			{
				printf("%d %d\n",i,q.front());
				fflush(stdout);
				scanf("%d",&ret);
				if (ret) ans[++m]=i,ans[++m]=q.front(),q.pop();
					else q.push(i);
			}
		for (int i=2*m;i>=1;i-=2)
			ans[i]=ans[i/2],ans[i-1]=-1;
		if (q.size())
		{
			bool last=1;
			for (int i=2;i<=2*m;i+=2)
			{
				printf("%d %d\n",q.front(),ans[i]);
				fflush(stdout);
				scanf("%d",&ret);
				if (ret && last) ans[i-1]=q.front(),q.pop();
				if (!q.size()) break;
				last=ret;
			}
		}
		if (q.size()) printf("-1\n");
		else
		{
			printf("%d\n",n);
			for (int i=1;i<=2*m;i++)
				if (ans[i]!=-1) printf("%d ",ans[i]);
			printf("\n");
		}
		fflush(stdout);
	}
	return 0;
}
posted @ 2020-11-01 14:54  stoorz  阅读(67)  评论(0编辑  收藏  举报