正题
题目链接:http://pjudge.ac/problem/21651
题目大意
有一个\(1\sim n\)的排列,每次你可以询问
- \(i\)和\(j\)的大小关系
- \(i,j,k\)的中位数
现在要求在\(2\)次\(1\)操作和\(2n\)次\(2\)操作内得到这个排列。
\(50\leq n\leq 5\times 10^5\)
解题思路
首先我们发现在询问中位数的情况下我们是完全不能够区分\(1,2\)和\(n-1,n\)的,那么两次操作\(1\)肯定是用于区分这两个东西上的。
那么我们假设我们现在已经得到了两个数字\([l,r]\),此时我们通过询问中位数\(l,r,x\)。
- 若\(l<x<r\),此时我们可以得到\(x\)。
- 得到\(x<l\)或者\(x>r\)
再进一步,如果我们不知道\(l,r\)的值呢。此时如果我们在询问中得到了两个\(k\),那么说明\(k\)一定是\(l\)或者\(r\)的值,也就是\(l\)和\(r\)我们能得到两次,如果有一个只得到了一次,那么说明\(l=1\)或者\(l=2\),也就是在边界附近的两格。
而如果我们已经确定了一个\(x<l\),此时肯定也有前一个\(y<l\)(因为\(l\)被得到了两次),那么我们令\(l=x\),然后再重新处理一次\(y\)。
不难发现这样最多扩展\(n\)次,每次会重新处理一个数字,也就是\(2n\)次询问。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include "guess.h"
#define mp(x,y) make_pair(x,y)
using namespace std;
const int N=5e5+10;
int v[N];
vector<int> now,ans;
vector<int> q;
void Clear(int l,int r){
for(int i=l;i<=r;i++)
if(v[i])ans[v[i]-1]=i,v[i]=0;
q.clear();return;
}
int Ask2(int x,int y,int z)
{return query2(x-1,y-1,z-1);}
int Ask1(int x,int y)
{return query1(x-1,y-1);}
vector<int> solve(int n,int m){
int x=1,y=2,L=0,R=0,flag=0;ans.resize(n);
for(int i=3;i<=n;i++)now.push_back(i);
for(int ii=0;ii<now.size();ii++){
// if(ans[now[ii]-1])continue;
int i=now[ii],k=Ask2(x,y,i);
if(v[k]){
if(flag==-1){
if(k>=R){
ans[y-1]=k;y=i;
now.push_back(v[k]);v[k]=0;
}
else{
ans[x-1]=k;x=i;
now.push_back(v[k]);v[k]=0;
}
}
else if(flag){
L=R=k=Ask2(i,v[k],x);
now.push_back(y);y=i;
if(k<flag)swap(x,y),R++;
// now.push_back(v[k]);v[k]=0;
// Clear(L+1,R-1);
for(int i=0;i<q.size();i++)
now.push_back(q[i]);
memset(v,0,sizeof(v));q.clear();
flag=-1;
}
else{
if(!flag)now.push_back(x);flag=k;x=i;
for(int i=0;i<q.size();i++)now.push_back(q[i]);
memset(v,0,sizeof(v));q.clear();
}
}
else v[k]=i,q.push_back(i);
}
Clear(3,n-2);
int a=v[2],b=v[n-1];
if(flag!=-1){
int k1=Ask2(a,b,x);
if(k1==n-1)swap(x,y);
}
if(Ask1(x,a))ans[x-1]=1,ans[a-1]=2;
else ans[a-1]=1,ans[x-1]=2;
if(Ask1(y,b))ans[y-1]=n-1,ans[b-1]=n;
else ans[b-1]=n-1,ans[y-1]=n;
return ans;
}