题解:AT_arc179_c [ARC179C] Beware of Overflow

题目传送门

首先,对于合并操作要求 \(|X_i+X_j|\le R\),这里 \(R\) 的大小是未知的(好像知道也没啥用),如果 \(X_i,X_j\) 是同号的话,说不好加起来就超了。

注意到,如果 \(X_i,X_j\) 是异号的话,由于有 \(|X_i|\le R,|X_j|\le R\),所以 \(|X_i+X_j|\le R\) 一定成立。

但是,我们似乎还是无法得到 \(X\) 的正负。考虑极端情况,设 \(X_i\) 为最小值,\(X_j\) 为最大值。这两个数显然是最有可能异号的。

如果 \(X_i,X_j\) 同号,那么所有的 \(X\) 同号,再加上 \(|\sum X|\le R\),所以 \(|X_i+X_j|\le R\),可以合并。

如果 \(X_i,X_j\) 异号,那么就满足条件。

想要获取最大值和最小值的位置,正好我们可以问 \(X\) 之间的相对大小,考虑使用这个作为比较函数排序(交互题老套路了)。

这样,只要我们不断合并最大值和最小值,就可以完成此题。

但还存在一个问题,合并会产生新的 \(X\),我们不能每次都排一遍。其实只要二分它的排名再插入即可,使用 vector 来维护所有位置的相对大小。

次数为 \(2n\log n+n-1\),时间复杂度 \(O(n^2)\)

#include <bits/stdc++.h>
using namespace std;
const int N=1005;
int T;
inline int cmp(int x,int y){
    assert(T<25000);++T;
    cout<<"? "<<x<<" "<<y<<endl;
    int res;cin>>res;assert(res!=-1);
    return res;
}
inline int add(int x,int y){
    assert(T<25000);++T;
    cout<<"+ "<<x<<" "<<y<<endl;
    int res;cin>>res;assert(res!=-1);
    return res;
}
int n,p[N],q[N];
vector <int> rk;
void merge_sort(int l,int r){
    if (l>=r) return ;
    int mid=l+r>>1;
    merge_sort(l,mid),merge_sort(mid+1,r);
    int i=l,j=mid+1,k=0;
    while (i<=mid&&j<=r)
        if (cmp(p[i],p[j])) q[k++]=p[i++];
        else q[k++]=p[j++];
    while (i<=mid) q[k++]=p[i++];
    while (j<=r) q[k++]=p[j++];
    for (int i=l,j=0;i<=r;i++,j++) p[i]=q[j];
}
int main(){
    cin>>n;
    for (int i=1;i<=n;i++) p[i]=i;
    merge_sort(1,n);
    for (int i=1;i<=n;i++) rk.push_back(p[i]);
    while (rk.size()>1){
        int x=*rk.begin(),y=*rk.rbegin(),z=add(x,y);
        rk.pop_back(),rk.erase(rk.begin());
        int l=0,r=rk.size()-1,mid,ans=rk.size();
        while (l<=r){
            mid=l+r>>1;
            if (cmp(z,rk[mid])) r=mid-1,ans=mid;
            else l=mid+1;
        }
        rk.insert(rk.begin()+ans,z);
    }
    cout<<"!"<<endl;
    return 0;
}
posted @ 2026-04-09 15:22  TP2010  阅读(8)  评论(0)    收藏  举报