P7843 「PMOI-4」猜排列 題解
Subtask 1
-
我們先通過一次二操作(注意 4 是序列中的最大值)得到 4 所在的位置。
-
由於 4%3=1, 4%2=4%1=0,所以我們可以通過 2 或 3 次一操作得到 3 所在的位置。
-
由於 3%2=1, 3%1=0,最後只需要多出 1 次一操作就能得到 1 與 2 的位置。
-
於是,我們總共使用 4 次一操作與 1 次二操作得到了整個序列。
Subtask 2
-
首先我們可以維護出對於每一個a[i]進行操作2所得到的序列s,也就是說我們得到了>=a[i]的所有序列元素的下標。
-
顯然,i 所在的位置就是 s[i] 與 s[i+1] 的差集
-
於是我們通過 n 次二操作查詢出了整個序列
好了,20pts夠了,本次講課到此結束
Subtask 3
隨機化
不會
Subtask 4
-
假設我們已經確定了所有 2 的次冪所在的位置,那麼我們就能通過 log n 次二操作與 n 次一操作得到整個序列
-
我們先通過 log n 次二操作,對於每一個 p 都求出小於 a[p] 的最大 2 的次冪所在位置 b[p]。然後,對於每一個 p 查詢 a[p]% a[b[p]],令其為 x,那麼 a[p]=a[b[p]]+x。
-
如何確定2的次冪的位置呢
-
我們只需要列舉一個 2 的次冪 k,然後查詢 s[k+1] 與 s[k],將二者做差就能得到各個 2 的次冪所在位置了
好! 很有精神!
Subtask 5
打標記即可
Subtask 6
-
首先,我們採用與Subtask5類似的方式,僅對於每一個 2 的次冪 p 求出 s[p](注意,這裡不求出 s[p+1])
-
我們從大到小列舉 p。令當前掃描到了 p1,上一次掃描到的是 p2 ,那麼我們先將 s[p1] 改為 s[p1]-s[p2](即,求差集),再將 p2 對這些位置分別取模;顯然,其中模數為 0 的那一個就是 p 所在的位置
-
我們用 n 次一操作,log n+1 次二操作得到了整個序列
Subtask 7
-
回顧Subtask 1
我們發現可以用更少的次數完成 -
然後就做完了
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;
}