马球比赛

Problem G: 马球比赛

Time Limit: 10 Sec Memory Limit: 128 MB

Description

在解决了马语翻译问题后,马匹数量越来越多,不少乡镇都有了数量可观的马匹,开始出现马球比赛。乡镇之间决
定进行马球联赛。联赛的赛制,主要是比赛双方的马匹数量,成了一个急需解决的问题。首先,所有乡镇都要求,
本乡镇所有的马匹都必须参赛,或者都不参赛(若组队的马匹数量不是该镇马匹数量的约数,将无法参赛)。其次
,在本乡镇,选出最佳球队,参加乡镇间联赛。现在,比赛组织方希望满足所有参赛乡镇的要求,并且使得决赛的
马匹尽可能多,请你设计每个球队马匹的数量,使得决赛马匹数最大。注意,决赛至少有2个队伍晋级。

Input

第一行一个整数N,表示想要报名参赛的乡镇。
接下来N个用空格分开的整数a(i),表示第i个乡镇报名参赛的马匹数。
2<=N<=200000,1<=a(i)<= 2000000。

Output

计算出决赛最大参与的马匹数。

Sample Input

3 
1 3 6 

Sample Output

6

【样例解释】
每个队伍3匹马,乡镇1无法参赛。乡镇2和3都可以进行比赛,决赛2个队伍,共6匹马。

HINT

1.题意分析:

首先,我们通过看题目的文字就很懵逼,[本蒟蒻根本就不知道在讲什么],但是看一下数据,就可以猜测一下题意:
Sample Input

3 
1 3 6 

Sample Output

6

这里的6是由2*3构成的,因为3和6都拥有共同的因数3,那么两个马球队都可以每场派出3匹马,则2个马球队各派3匹马则可使Ans最大为6。

[
1,3,6的最大公因数为1,Ans=1 * 3=3
3,6的最大公因数为3,Ans=3 * 2=6
故Ans最大为6。
]

那么我们依稀看见了求因数的影子,所以我们需要对每个马球队的马匹求其因子。也就是说这n个数中,任m(m<=n)个数都会有共有的因数,要我们求的是这n个数中,m个数的最大公因数*m最大值是多少。
那么我们就需要用一个数组来维护每一个因数(因数<=max(a[1…n]))在这n个数中是多少个数的因数。[很绕,自行体会]
这里的话就不多废话,代码实现很简单:

scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%lld",&a);
        int k=sqrt(a);
        for (int j=2;j<=k;j++)
            if (!(a%j))
            {
                f[j]++;
                if (j*j!=a) f[a/j]++;//Very important
            }
        f[a]++,mos=max(mos,a);//这里f[a]++指本身也是其本身的因数,同样用mos表示最大值,最后在求答案中有用
    }

注意从2 to sqrt(a)来枚举,这样的话,每找到一个因数就意味着这个数除以这个因子也是同样是这个数因数,但要注意:
这个数不能是j的平方,如果是这样的话,就会使f[j]的个数翻倍。
也就是if语句所起到的作用。
那么当我们将所有输入的马球队的马匹数量都处理完后,我们就需要开始找答案(Ans打擂台)。
同样也很简单:

    f[1]=n;//Very important,too
    for (int i=1;i<=mos;i++)//从1到最大值开始枚举因数
        if (f[i]>1&&f[i]*i>ans) ans=f[i]*i; //判断

同样的我们也要注意:
注意,决赛至少有2个队伍晋级。
则f[i]必须大于1,所以if很重要。
那么f[1]=n是个什么鬼???
这里我们要考虑一下一种极端情况:
Sample Input

3
1 3 5

答案肯定为3啊!两两互质,则每个马球队都只能出1匹马。
所以f[1]=n就是指上述情况。

等等,还没完!!!
让我们看一看数据范围:
2<=N<=200000,1<=a(i)<= 2000000
如果极端数据的话,Ans能等于400000000000!
int: -2147483648~2147483647
完爆int,所以要开long long。
[本蒟蒻就因为这一点WA了N次。。。]

那么这样我们就很“轻松”地把这道题AC了。

最终代码:

#include <bits/stdc++.h>
#pragma GCC optimize(2)//O2优化,不用在意
using namespace std;
 
int n,mos,a;
long long ans;//
long long f[2000001];
 
int main()
{
//  freopen("polo.in","r",stdin);
//  freopen("polo.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%lld",&a);
        int k=sqrt(a);
        for (int j=2;j<=k;j++)
            if (!(a%j))
            {
                f[j]++;
                if (j*j!=a) f[a/j]++;
            }
        f[a]++,mos=max(mos,a);
    }
    f[1]=n;
    for (int i=1;i<=mos;i++)
        if (f[i]>1&&f[i]*i>ans) ans=f[i]*i;
    printf("%lld",ans);
    return 0;
}

当然还有更简单的,仅供大家参考(其实不简单,也就剩了点时间):

#include <cmath>
#include <cstdio>
#pragma GCC optimize(2)
using namespace std;
 
int n,h,max;
long long ans,f[2000001];
 
void init()
{
    scanf("%d",&n);
    for (int i=0;i<n;i++)
    {
        scanf("%d",&h),f[h]++;
        if (h>max) max=h;
    }
}
 
void solve()
{
    ans=n;
    for (int i=2;i<=max;i++)
        if (f[i])
        {
            int k=sqrt(i);
            for (int j=2;j<=k;j++)
                if (!(i%j))
                {
                    f[j]+=f[i];
                    if (j*j!=i) f[i/j]+=f[i];
                }
        }
    for (int i=1;i<=max;i++)
        if (f[i]>1&&f[i]*i>ans) ans=f[i]*i;
    printf("%lld",ans);
}
 
int main()
{
    init();
    solve();
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值