题解:AT_arc154_d [ARC154D] A + B > C ?

Luogu AT

可以发现 \(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;
}
posted @ 2025-07-21 14:20  FugiPig  阅读(8)  评论(0)    收藏  举报