题解: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;
}

浙公网安备 33010602011771号