P7843 「PMOI-4」猜排列 题解

Subtask 1


  1. 我们先通过一次二操作(注意 4 是序列中的最大值)得到 4 所在的位置。

  2. 由于 4%3=1, 4%2=4%1=0,所以我们可以通过 2 或 3 次一操作得到 3 所在的位置。

  3. 由于 3%2=1, 3%1=0,最后只需要多出 1 次一操作就能得到 1 与 2 的位置。

  4. 于是,我们总共使用 4 次一操作与 1 次二操作得到了整个序列。


Subtask 2


  1. 首先我们可以维护出对于每一个a[i]进行操作2所得到的序列s,也就是说我们得到了>=a[i]的所有序列元素的下标。

  2. 显然,i 所在的位置就是 s[i] 与 s[i+1] 的差集

  3. 于是我们通过 n 次二操作查询出了整个序列

好了,20pts够了,本次讲课到此结束


Subtask 3


随机化

不会


Subtask 4


  1. 假设我们已经确定了所有 2 的次幂所在的位置,那么我们就能通过 log n 次二操作与 n 次一操作得到整个序列

  2. 我们先通过 log n 次二操作,对于每一个 p 都求出小于 a[p] 的最大 2 的次幂所在位置 b[p]。然后,对于每一个 p 查询 a[p]% a[b[p]],令其为 x,那么 a[p]=a[b[p]]+x。

  3. 如何确定2的次幂的位置呢

  4. 我们只需要枚举一个 2 的次幂 k,然后查询 s[k+1] 与 s[k],将二者做差就能得到各个 2 的次幂所在位置了

好! 很有精神!


Subtask 5


打标记即可


Subtask 6


  1. 首先,我们采用与Subtask5类似的方式,仅对于每一个 2 的次幂 p 求出 s[p](注意,这里不求出 s[p+1])

  2. 我们从大到小枚举 p。令当前扫描到了 p1,上一次扫描到的是 p2 ,那么我们先将 s[p1] 改为 s[p1]-s[p2](即,求差集),再将 p2 对这些位置分别取模;显然,其中模数为 0 的那一个就是 p 所在的位置

  3. 我们用 n 次一操作,log n+1 次二操作得到了整个序列


Subtask 7


  1. 回顾Subtask 1
    我们发现可以用更少的次数完成

  2. 然后就做完了

Code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <stdlib.h>
#include <stack>
#include <queue>
#define ri register int

using std::min;
using std::max;

inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}

int Up(int x,int y){
	if(x%y==0)return x/y;
	else return x/y+1;
}

const int N=5e4+5;
int n,m1,m2,m3;
int ans[N],vis[N],whe[N];
int a[N],b[N],las,lasp;

int Query1(int x,int *d){
	printf("? ");
	int sum=0;
	for(ri i=1;i<=n;i++){
		if(!ans[i])sum++; 
	}
	printf("%d ",sum);
	for(ri i=1;i<=n;i++){
		if(!ans[i])printf("%d ",i);
	}
	printf("%d ",x);fflush(stdout);
	printf("\n");fflush(stdout);
	int k=read();
	for(ri i=1;i<=k;i++)d[i]=read();
	return k;
}

int c;

int Find(int x){
	for(ri i=1;i<=n;i++)a[i]=b[i]=0;
	c=Query1(x,a),c=Query1(x+1,b);
	std::sort(a+1,a+c+2),std::sort(b+1,b+c+1);
	for(ri i=1;i<=c+1;i++){
		if(a[i]!=b[i]){
			ans[a[i]]=x;whe[x]=a[i];
			return a[i];
		}
	}
}



int Query2(int x,int y){
	printf("! %d %d\n",x,y);fflush(stdout);
	int k=read();
	return k;
}

int qwq(int x,int y){
	if(!x)return y;
	else return x;
}

int Find2(int x){
	for(ri i=1;i<=n;i++)a[i]=0;
	int d=Query1(x,a),p=1;
	std::sort(a+1,a+d+1);
	for(ri i=1;i<=d;i++){
		int k=Query2(lasp,a[i]);
		if(k==0)ans[a[i]]=x,whe[x]=a[i];
		else ans[a[i]]=las+1-k,whe[las+1-k]=a[i];
	}
	for(ri i=1;i<=n;i++){
		if(ans[i]==x)return i;
	}
}

int main(){
	n=read(),m1=read(),m2=read(),m3=read();
	las=n;
	bool fir=1;
	int k;
	while(1){
		k=Up(las+1,2);
		if(las<=4)break; 
		int p;
		if(fir)p=Find(k); //c是短 
		else p=Find2(k);
		if(fir){
			for(ri i=1;i<=c;i++){
		    	int l=Query2(b[i],p);
		    	ans[b[i]]=k+qwq(l,k);
		    	whe[k+qwq(l,k)]=b[i];
	    	}
		}
		int flag=1,mi=1e9+7;
		for(ri i=1;i<=n;i++){
			if(!ans[i])flag=0;
			else mi=min(mi,ans[i]);
//			printf("%d ",ans[i]);
		} 
		if(flag)break;
		las=mi-1;fir=0;lasp=p;
//		printf("\n");
	}
	if(!whe[4]){
		k=Query1(4,a);
		k=a[1];
		whe[4]=k,ans[k]=4;
		int res=0;
		for(ri i=1;i<=n;i++){
			if(!ans[i]){
				int l=Query2(whe[4],i);
				if(l==1){
					ans[i]=3,whe[3]=i;break;
				}
				res++;
			}
			if(res==2){
				for(ri j=i+1;j<=n;j++){
					if(!ans[j]){
						ans[j]=3,whe[3]=j;break;
					}
				}
				break;
			}
		}
		int x=0,y=0;
		for(ri i=1;i<=n;i++){
			if(!ans[i]){
				if(!x)x=i;
				else y=i; 
			}
		}
		k=Query2(whe[3],x);
		if(k)ans[x]=2;
		else ans[y]=2;
	}
	else if(!whe[3]){
		int res=0;
		for(ri i=1;i<=n;i++){
			if(!ans[i]){
				int l=Query2(whe[4],i);
				if(l==1){
					ans[i]=3,whe[3]=i;break;
				}
				res++;
			}
			if(res==2){
				for(ri j=i+1;j<=n;j++){
					if(!ans[j]){
						ans[j]=3,whe[3]=j;break;
					}
				}
				break;
			}
		}
		int x=0,y=0;
		for(ri i=1;i<=n;i++){
			if(!ans[i]){
				if(!x)x=i;
				else y=i; 
			}
		}
		k=Query2(whe[3],x);
		if(k)ans[x]=2;
		else ans[y]=2;
	}
	else if(!whe[2]){
		int x=0,y=0;
		for(ri i=1;i<=n;i++){
			if(!ans[i]){
				if(!x)x=i;
				else y=i; 
			}
		}
		k=Query2(whe[3],x);
		if(k)ans[x]=2;
		else ans[y]=2;
	}
	printf("A ");
	for(ri i=1;i<=n;i++){
		if(ans[i])printf("%d ",ans[i]);
		else printf("1 ");
	}
	fflush(stdout);
	return 0;
}


posted @ 2021-10-06 16:07  Konjac-Simit  阅读(117)  评论(0)    收藏  举报