题解:P9721 [EC Final 2022] Inversion

· · 题解

新教练布置的题,挺有意思的。我的做法不太用预处理很多,常数挺小的,没人写过就写个题解。

首先考虑 i-1i 的过程,本质上是要在原先确定好顺序的前 1 \sim i-1 项中插入第 i 项,即确定 i 在前 i-1 中是第几小。

很容易想到从第一小开始依次比较,但是至少 n^2 问询,无法接受。于是想到可以二分找第 k 小,得到了 n \log_2 n 的做法。

问题来了,怎么通过题设交互方法实现任意位置 i,j (i<j) 两数的大小比较?

至此,题目迎刃而解,感觉思路还是挺自然的,不会很复杂,实现也挺简单,很小清新的题。

#include<bits/stdc++.h>
using namespace std;
const int _=2005;
int n;
int sum[_],rk[_],id[_];
int main(){
    cin>>n;
    rk[1]=1;
    for(int i=2;i<=n;++i){
        int l=1,r=i-1,ans=0;
        while(l<=r){
            int mid=(l+r)>>1;
            int q1,q2;
            cout<<"? "<<rk[mid]<<" "<<i<<endl;
            cin>>q1;
            if(rk[mid]==i-1){
                if(q1) r=mid-1;
                else ans=mid,l=mid+1;
            }
            else{
                cout<<"? "<<rk[mid]+1<<" "<<i<<endl;
                cin>>q2;
                if((q2+sum[rk[mid]])%2==q1) ans=mid,l=mid+1;
                else r=mid-1;
            }
        }
        for(int j=i;j>ans+1;--j) rk[j]=rk[j-1];
        rk[ans+1]=i;
        for(int j=ans+2;j<=i;++j) ++sum[rk[j]];
    }
    for(int i=1;i<=n;++i) id[rk[i]]=i;
    cout<<"! ";
    for(int i=1;i<=n;++i) cout<<id[i]<<" ";
    cout<<endl;
    return 0;
}