P7045-[MCOI-03]金牌【構造,互動題】

Quant-Ask發表於2020-10-31

正題

題目連結:https://www.luogu.com.cn/problem/P7045?contestId=36089


題目大意

n n n個物品有一些顏色,可以詢問 Q Q Q次兩個物品的顏色是否相同,求一個排列是的相鄰的物品顏色不同。其中 Q ≥ 2 n − 2 Q\geq 2n-2 Q2n2


解題思路

對於每一個不在頭尾的物品,我們需要求出兩個與其顏色不同的物品,當我們判斷兩個物品顏色是否相同時,如果不同,那麼我們就各為一個物品找到了一個顏色不同的。如果相同,假設有 k k k個顏色相同的物品,那麼只需要找到 k + 1 k+1 k+1個顏色與他們不同的物品,也就是沒浪費一個詢問找到相同的物品,後面就可以少用一次詢問找不同的物品。所以可以證明如果有解的話那麼一定在 2 n − 2 2n-2 2n2次可以詢問出答案。

考慮如何實現,我們開一個棧,如果新的物品和棧頂的顏色相同,那麼壓入棧中。否則在序列後面填入一個棧頂元素,如果此時棧為空那麼將新的物品壓入棧中,否則直接填在後面。

對於剩下棧中的元素,我們從前面往後開始找位置填入即可。

時間複雜度 O ( T n ) O(Tn) O(Tn)


c o d e code code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
const int N=5e4+10;
int T,n,Q,v[N],z[N],a[N],cnt;
stack<int> s;
int Ask(int x,int y){
	int ans;
	printf("%d %d\n",x-1,y-1);
	fflush(stdout);
	scanf("%d",&ans);
	return ans;
}
int main()
{
	scanf("%d",&T);v[0]=1;
	while(T--){
		scanf("%d%d",&n,&Q);
		for(int i=1;i<=n;i++)
			v[i]=z[i]=0;
		while(!s.empty())s.pop(); 
		cnt=0;s.push(1);
		int lim=1;
		for(int i=2;i<=n;i++){
			bool z=Ask(s.top(),i);
			if(z){
				a[++cnt]=s.top();s.pop();
				if(s.empty())s.push(i),lim=i;
				else a[++cnt]=i;
			}
			else s.push(i);
		}
		a[++cnt]=s.top();s.pop();
		if(s.empty()){
			printf("%d\n",n); 
			for(int i=1;i<=cnt;i++)
				printf("%d ",a[i]-1);
			putchar('\n');
			fflush(stdout);
			continue;
		}
		for(int i=1;i<lim;i++){
			v[i]=Ask(a[i],s.top());
			if(v[i]&&v[i-1]){
				z[i]=s.top();
				s.pop();
				if(s.empty())break;
			}
		}
		if(!s.empty()){printf("-1\n");fflush(stdout);continue;}
		printf("%d\n",n);
		for(int i=1;i<=cnt;i++){
			if(z[i])printf("%d ",z[i]-1);
			printf("%d ",a[i]-1);
		}
		putchar('\n');
		fflush(stdout);
	}
}

相關文章