2017.7.13 NOIP2017赛前模拟考试总结

本次考试三道题是以前做过的模板题,难度均不大,T1主要是注意数组开long long ,T2则是一道裸的LCA,再次就不赘述,主要说说T3

给定 n 个数,求最大的数 m ,使得 m 是 n 个数中至少一半的数的约数。
注意:m 不一定在 n 个数中,只要满足要求即可。
数据范围:
对 40% 的输入数据 : n≤100
对 100% 的输入数据 :n≤100000;1≤数字的大小≤10^12

我们容易想到枚举每个数的约数,然后再统计每个约数出现的次数,直接check 条件就好,但是这样只能过40%的数据。
正解:
我们考虑每次随机一个数字,那么答案是这个数的约数的概率为1/2,那么我们只需要随机取10—20次,错误的概率就会趋近于0;
现在问题变为如何判断一个数的约数再其他数字中出现的次数,首先暴力肯定是不行的,我们考虑两个数字x,y,我们可以断言满足条件的约数一定是gcd(x,y)的约数,那么我们只要统计和这个数的每种不同的gcd出现了多少次;对于指定的约数k,我们枚举他的倍数1,累加1作为gcd的次数,sum即为k在这些数字里面一共出现了多少次

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
long long n;
long long a[100001];
long long num[4000001];
long long tot;

map <long long,bool> m; //m表示这个gcd是否出现过

bool check(long long x)
{
    long long u=0;
    long long i;
    for(i=1;i<=n;i++)
      {
        if(!(a[i]%x)) u++;
      }
    if(u*2>=n) return true;
    return false;
}

long long read()
{
    long long k=0,f=1;
    char c=getchar();
    while(c>'9'||c<'0') {if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9') {k=k*10+(c-'0'); c=getchar();}
    return k*f;
}

int main()
{
    //freopen("half.in","r",stdin);
    //freopen("half.out","w",stdout);
    long long i,j,k;

    n=read();
    for(i=1;i<=n;i++)
      a[i]=read();
    srand(time(0));  
    long long s,t;
    for(int jj=1;jj<=20;jj++)  
    {
        j=rand()%n+1;
    for(i=1;i<=sqrt(a[j]);i++)  
      {

        if(!(a[j]%i)) 
          {
           if(!m[i]) {m[i]=1; num[++tot]=i;}    
           if(!m[a[j]/i]) {m[a[j]/i]=1;num[++tot]=a[j]/i;}
          }
      }
    }
    long long ans=1;
    for(i=1;i<=tot;i++)
      {
        if(check(num[i]))
          ans=max(ans,num[i]);
      }
    cout<return 0;
}

你可能感兴趣的:(考试总结)