BZOJ2797. [Poi2012]Squarks
Description
设有n个互不相同的正整数{X1,X2,...Xn},任取两个Xi,Xj(i≠j),能算出Xi+Xj。
现在所有取法共n*(n-1)/2个和,要你求出X1,X2,...Xn。
Input
第一行一个正整数n (3<=n<=300)。
第二行n*(n-1)/2个正整数(每个正整数不超过10^8),表示任取两个Xi,Xj(i≠j)算出的n*(n-1)/2个和。
Output
第一行一个正整数k,表示方案数。测试数据保证至少存在一种方案。
下面k行每行给出递增的n个正整数。方案按照{Xi}的最小值从大到小输出。
Sample Input
Sample Input 1
4
3 5 4 7 6 5
Sample Input 2
4
11 17 12 20 21 15
Sample Output
Sample Output 2
2
4 7 8 13
3 8 9 12
Sample Output 1
1
1 2 3 4
令a1<a2<a3<......显然给定的数组中最小的是a1+a2,次小的是a1+a3,那么根据一些基础的线性代数知识,我们只要枚举a2+a3,这样我们就可以得到a1,a2,a3。现在考虑知道了a1,a2...ak,那么现在我们已经有了(k+1)*k/2个和,我们考虑剩下的数中,最小的一定是a1+a_k+1,那么我们就知道了a_k+1,multiset实现(你要想写平衡树我不拦你)即可。
比较难过的是此题需要一定的卡常。(同样的算法,neither_nor大爷过了,我T了,坑爹啊)
#include<bits/stdc++.h> using namespace std; const int N=305; int ans[N][N],b[N*(N-1)/2+2],cnt=0,m,n,a[N],pd=0; inline int read(){ int x=0,f=1; char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1; ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();} return x*f; } multiset<int> s; inline void solve(int p){ if (pd==0) s.clear(); for (register int i=3;i<=m;i++){ if (i==p) continue; s.insert(b[i]); } multiset<int>::iterator it; for (register int i=4;i<=n;i++){ int q=*s.begin(); a[i]=q-a[1]; if (a[i]<=a[i-1]) { pd=0; return;} for (register int j=1;j<i;j++){ it=s.find(a[i]+a[j]); if (*it!=a[i]+a[j]){pd=0; return;} s.erase(it); } } cnt++; for (register int i=1;i<=n;i++) ans[cnt][i]=a[i]; pd=1; } int main(){ n=read(); m=n*(n-1)/2; for (register int i=1;i<=m;i++) b[i]=read(); sort(b+1,b+1+m); for (register int i=3;i<=m;i++){ if (i>3&&(b[i]==b[i-1])) continue; int t=b[i]; if ((t-b[2]+b[1])%2==1) continue; a[2]=(t-b[2]+b[1])/2; a[1]=b[1]-a[2]; a[3]=t-a[2]; if (a[1]<0||a[2]<0||a[3]<0) continue; if (a[2]<=a[1]||a[2]>=a[3]||a[1]>=a[3]) continue; solve(i); } printf("%d\n",cnt); for (register int k=1;k<=cnt;k++){ for (register int i=1;i<=n;i++) printf("%d ",ans[k][i]); printf("\n"); } return 0; }

浙公网安备 33010602011771号