正题
题目链接:http://pjudge.ac/problem/21651
题目大意
有一个1∼n1\sim n1∼n的排列,每次你可以询问
- iii和jjj的大小关系
- i,j,ki,j,ki,j,k的中位数
现在要求在222次111操作和2n2n2n次222操作内得到这个排列。
50≤n≤5×10550\leq n\leq 5\times 10^550≤n≤5×105
解题思路
首先我们发现在询问中位数的情况下我们是完全不能够区分1,21,21,2和n−1,nn-1,nn−1,n的,那么两次操作111肯定是用于区分这两个东西上的。
那么我们假设我们现在已经得到了两个数字[l,r][l,r][l,r],此时我们通过询问中位数l,r,xl,r,xl,r,x。
- 若l<x<rl<x<rl<x<r,此时我们可以得到xxx。
- 得到x<lx<lx<l或者x>rx>rx>r
再进一步,如果我们不知道l,rl,rl,r的值呢。此时如果我们在询问中得到了两个kkk,那么说明kkk一定是lll或者rrr的值,也就是lll和rrr我们能得到两次,如果有一个只得到了一次,那么说明l=1l=1l=1或者l=2l=2l=2,也就是在边界附近的两格。
而如果我们已经确定了一个x<lx<lx<l,此时肯定也有前一个y<ly<ly<l(因为lll被得到了两次),那么我们令l=xl=xl=x,然后再重新处理一次yyy。
不难发现这样最多扩展nnn次,每次会重新处理一个数字,也就是2n2n2n次询问。
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;
}