输入
输出
样例输入
4
1 1 2 2 3 4 6 2 2 1 3 2 2 1 3 2
样例输出
6 4 3 2
数据范围
分析
很显然,A序列其实就是一个N*N的矩阵,
也可以认为将一个N*N的矩阵变成A序列,然后打乱顺序。
那么,我们就将A序列看成一个矩阵吧。
答案很显然就是这个矩阵的对角线上面的数。
因为gcd(a,b)=gcd(b,a)的
所以这个矩阵是关于对角线对称的。
我们知道gcd(a,b)≤a和gcd(a,b)≤b
而且B序列是从大到小的,
所以 B1 一定是矩阵中最大的数,
B2 一定是矩阵中第二大的数,
- 那么, B3 是矩阵第三大的数吗?
我们想一下,
我们已经知道了 B1 和 B2
那么, gcd(B1,B2) 我们也就知道了。
也就是说,在A序列中间的一些数的位置就可以确定下来了。
所以,我们就可以将这些数从A序列中间删除。
- 那怎样删除这些数字了?
如果改成-1是不行的,因为我们需要二分来查找gcd(a,b)
我们知道在排序之后,所有相同的数都会堆在一起。
那么,我们就可以在每一种数第一次出现的地方,
记录题目已经用了几个。
在删除完全部已知的数之后,最大的值就是下一个B序列的值。
每当B序列加入一个数,
我们就将这个数和前面B序列的数gcd一下,
把这些最大公约数从A序列中间删除,
这里需要删除两个。
code(c++)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <stdlib.h>
#include <math.h>
using namespace std;
int a[1000003],fly[1000003],z[1003];
int n,sum;
bool cmp(int x,int y)
{
return x>y;
}
int gcd(int x,int y)
{
if(x%y==0)return y;
else return gcd(y,x%y);
}
int find(int x)
{
int l=3,r=n*n,mid=0;
while(l<r)
{
mid=(l+r)/2;
if(a[mid]>x)l=mid+1;else r=mid;
}
return l;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n*n;i++)
{
scanf("%d",&a[i]);
fly[i]=i;
}
sort(a+1,a+1+n*n,cmp);
z[1]=a[1];
z[2]=a[2];
int t=gcd(z[1],z[2]);
int k=find(t);
fly[k]+=2;
int x=3;
for(int i=3;i<=n;i++)
{
while(a[fly[x]]!=a[x])
x=fly[x];
z[i]=a[x];
t=find(z[i]);
fly[t]++;
for(int j=1;j<i;j++)
{
t=gcd(z[j],z[i]);
k=find(t);
fly[k]+=2;
}
}
for(int i=1;i<=n;i++)
printf("%d ",z[i]);
}