Description Input Output Sample Input 4 1 1 2 2 3 4 6 2 2 1 3 2 2 1 3 2 Sample Output 6 4 3 2
The Solution
首先,序列中的数对应的就是 Gcd 矩阵中对角线的数字,所以一定是存 在于矩阵中的。有一个显然的性质就是 Gcd(a,b) <= a、b,我们设还原后的 序列从大到小排序后为 B数组。那么 B[1]一定就是矩阵中最大的数, B[2]一定是矩阵中第二大的数。考虑 B[3]是否也满足这个性质。我发现 矩阵中可能比他大的就只有 Gcd(B[1],B[2])。那么每取出一个 B[i], 把 B[i]以及与 B[1~i-1]的 Gcd去掉后,后面的数取最大就可以了。 gcd要去掉2个。因为这矩阵是对称的。
那怎样删除这些数字了? 我们需要二分来查找gcd(a,b) 我们知道在排序之后,所有相同的数都会堆在一起。 那么,我们就可以在每一种数第一次出现的地方, 记录题目已经用了几个。
这里说明一下lower_bound好像只能在升序序列上用…
CODE
using namespace std;
int a[N
*N],wz[N
*N],b[N];
int n;
inline
int read()
{
int x=
0,w=
1;
char ch=getchar();
while(ch>
'9'||ch<
'0'){
if(ch==
'-')w=-
1;ch=getchar();}
while(ch>=
'0'&&ch<=
'9'){
x=
x*10+ch-
'0';ch=getchar();}
return x*w;
}
bool cmp(
int x,
int y) {
return x>
y;}
int gcd(
int x,
int y) {
return x%y==
0?
y:gcd(
y,
x%y);}
int find(
int x)
{
int l=
3,r=n
*n;
while (l<r)
{
int mid=(l+r)>>
1;
if (a[mid]>
x) l=mid+
1;
else r=mid;
}
return l;
}
int main()
{
freopen(
"password1.in",
"r",stdin);
freopen(
"password.out",
"w",stdout);
n=
read();
fo(i,
1,n
*n)
{
a[i] =
read();
wz[i] = i;
}
sort(a+
1,a+
1+n
*n,cmp);
b[
1] = a[
1]; b[
2] = a[
2];
//int Pos=lower_bound(a+
3,a+n
*n+
1,gcd(b[
1],b[
2]))-a;
int Pos=find(gcd(b[
1],b[
2]));
wz[Pos]+=
2;
int x=
3;
fo(i,
3,n)
{
while (a[wz[
x]]!=a[
x])
x=wz[
x];
b[i]=a[
x];
// int k=lower_bound(a+
3,a+n
*n+
1,b[i])-a;
int k=find(b[i]);
wz[k]++;
fo(j,
1,i-
1)
{
int Pos=find(gcd(b[i],b[j]));
// int Pos=lower_bound(a+
3,a+n
*n+
1,gcd(b[i],b[j]))-a;
wz[Pos]+=
2;
}
}
fo(i,
1,n)
printf(
"%d ",b[i]);
}
转载请注明原文地址: https://ju.6miu.com/read-1311122.html