题解:AT_arc154_d [ARC154D] A + B > C ?
可以发现 \(1\) 很重要,因为 \(1+1\le所有其他数\),考虑找到 \(1\)。
通过双指针可 \(O(n)\) 找到 \(1\),维护当前猜想的答案 \(x\),依次遍历每个位置,如果 \(2P_x>P_i\),说明 \(x\) 一定不合法,于是令 \(x\gets i\) 即可。
又发现 \(1\) 有这样的性质:
\[\begin{cases}p_i<p_j\Leftrightarrow p_i+1\le p_j\\p_i>p_j\Leftrightarrow p_i+1>p_j\end{cases}
\]
所以找到 \(1\) 后利用这样的性质定义 cmp 就可以直接归并排序,不用快速排序的原因在于其询问次数带常数而且不稳定,总询问次数在 \(O(n\log n)\)。
注意特判 \(n=1\) 的情况,不进排序或排序中当 \(l\ge r\) 时直接返回。
Code:
#include<iostream>
#include<string>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,l,r) for(int i=(l);i>=(r);i--)
using namespace std;
const int maxn=2e3+5;
int as[maxn],ans[maxn],at[maxn];
int in,cp=1;
string s;
inline bool ask(int x,int y,int z){
cout<<'?'<<' '<<x<<' '<<y<<' '<<z<<endl;
cout.flush();
cin>>s;
return s[0]=='Y';
}
inline void solve(int l,int r){
if(l>=r)return;
int mid=(l+r)>>1;
solve(l,mid);
solve(mid+1,r);
int len=0,p1=l,p2=mid+1;
while(p1<=mid&&p2<=r){
if(ask(as[p1],cp,as[p2]))at[++len]=as[p2++];
else at[++len]=as[p1++];
}
while(p1<=mid)at[++len]=as[p1++];
while(p2<=r)at[++len]=as[p2++];
rep(v1,l,r)as[v1]=at[v1-l+1];
}
int main(){
ios::sync_with_stdio(true);
cin.tie(0);
cout.tie(0);
cin>>in;
rep(v1,2,in)if(ask(cp,cp,v1))cp=v1;
int tot=0;
rep(v1,1,in)if(v1!=cp)as[++tot]=v1;
solve(1,tot);
ans[cp]=1;
rep(v1,1,tot)ans[as[v1]]=v1+1;
cout<<'!';
rep(v1,1,in)cout<<' '<<ans[v1];
cout<<endl;
return 0;
}

浙公网安备 33010602011771号