[JZOJ 4699][CF583C]【NOIP2016提高A组模拟8.15】Password

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

#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define fd(i,a,b) for (int i=a;i>=b;i--)
#define N 1005
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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值