1. 程式人生 > 其它 >P7843 「PMOI-4」猜排列 題解

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;
}