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

浙公网安备 33010602011771号