[ARC154D]A+B>C?:交互题与经典算法的联系
[ARC154D] A + B > C ?
交互题。有一个未知的排列,你每次可以询问交互库
? a b c
,交互库会回答 \(p_a+p_b>p_c\) 是否成立,请在 \(n\log n\) 次询问中得到排列。
有三个参数实际上并不好做,如果有两个参数,那就相当于重载了大于号的运算符,这样的话,我们用基于比较的 \(O(n\log n)\) 排序算法即可得到原排列。
如何把三个参数变成两个参数?我们习惯性地去找 \(1\)(因为通常 \(1\) 是好找的),因为 \(p_a+1 >p_b\) 和 \(p_a>p_b\) 是完全等价的,因为这是个排列。
找 \(1\) 的过程可以在 \(O(n)\) 时间内完成:只有 \(1\) 满足 \(1+1>p_i\) 对所有 \(p_i\ne 1\) 都不成立。我们不断用当前可能为 \(1\) 的位置和一个未知的位置去比较,可以用双指针扫一遍得到。
#include <bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
int read(){
char c=getchar();int h=0,tag=1;
while(!isdigit(c)) tag=(c=='-'?-1:1),c=getchar();
while(isdigit(c)) h=(h<<1)+(h<<3)+(c^48),c=getchar();
return h*tag;
}
void fil(){
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
}
const int N=5e5+50;
int pos1;
bool cmp(int a,int b) { //[a<b]
cout<<"? "<<a<<" "<<pos1<<" "<<b<<endl;
string s;
cin>>s;
if(s=="Yes") {
return 0;
}else{
return 1;
}
}
int p[N];
void merge(int l,int r) {
if(l==r) {
return ;
}
int mid=(l+r>>1);
merge(l,mid);merge(mid+1,r);
vector<int>t;
int j,k;
for(j=l,k=mid+1;j<=mid&&k<=r;) {
if(cmp(p[j],p[k])) t.push_back(p[j]),j++;
else t.push_back(p[k]),k++;
}
if(j<=mid) for(;j<=mid;j++) {
t.push_back(p[j]);
}
if(k<=r) for(;k<=r;k++) t.push_back(p[k]);
int cnt=0;
for(int x:t) {
p[cnt+l]=x;cnt++;
}
}
int a[N];
int main(){
// fil();
int n=read();
int _i=1;
for( int i=1,k=2;i<=n&&k<=n;k++) {
_i=i;
cout<<"? "<<i<<" "<<i<<" "<<k<<endl;
string s;
cin>>s;
if(s=="Yes") {
i=k;
}else{
}
_i=i;
}
pos1=_i;
for(int i=1;i<=n;i++) p[i]=i;
merge(1,n);
for(int i=1;i<=n;i++) a[p[i]]=i;
cout<<"! ";for(int i=1;i<=n;i++) cout<<a[i]<<" ";
cout<<endl;
return 0;
}