原题: https://acm.ecnu.edu.cn/contest/126/problem/C/
题意:
有一个n长数组,两两相加得到一个n(n-1)/2的数组,给出这个n(n-1)/2的数组(排序后),求出原数组(任意解)
解析:
假设原数组从小到大为
,它们的和中,最小的一定是
,其次是
,但是
排第几不知道。
但是,我们知道
可以排到的最后面是第n位(前面是
),所以我们枚举
的名次,那么就可以解出
分别使什么,我们将这三个数的两两之和从原数组中删除,那么此时最小的就是
,那么就得到了
;再删除
和前面的数的和,以得到
……
在求 的时候有个细节,就是不能除二会出错
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define debug(i) printf("# %d\n",i)
int a[50100];
multiset<int>S;
multiset<int>::iterator it;
int ans[301];
bool fin(int idx,int n){
ans[1]=(a[1]+a[2]-a[idx])/2;
ans[2]=(a[1]-a[2]+a[idx])/2;
ans[3]=(-a[1]+a[2]+a[idx])/2;
if(a[1]+a[2]-a[idx]!=ans[1]*2||a[1]-a[2]+a[idx]!=ans[2]*2||-a[1]+a[2]+a[idx]!=ans[3]*2)return 0;
multiset<int>s(S);
if(ans[1]<0||ans[2]<0||ans[3]<0)return 0;
it=s.find(a[idx]);
s.erase(it);
for(int i=4;i<=n;i++){
ans[i]=*s.begin()-ans[1];
if(ans[i]<0)return 0;
s.erase(s.begin());
for(int j=2;j<i;j++){
it=s.find(ans[j]+ans[i]);
if(it==s.end())return 0;
s.erase(it);
}
}
return 1;
}
int main(){int t;scanf("%d",&t);while(t--){
S.clear();
int n;
scanf("%d",&n);
int nn=n*(n-1)/2;
for(int i=1;i<=nn;i++){
scanf("%d",a+i);
if(i>2)S.insert(a[i]);
}
for(int i=3;i<=n;i++){
if(fin(i,n))break;
}
for(int i=1;i<=n;i++){
printf("%d%c",ans[i],(i==n?'\n':' '));
}
}}