2017.7.13 NOIP2017赛前模拟考试总结

来源:互联网 发布:大尺度网络腐剧百度云 编辑:程序博客网 时间:2024/05/21 16:22

本次考试三道题是以前做过的模板题,难度均不大,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<cmath>#include<cstdlib>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<ctime>#include<map>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<<ans;    return 0;}
原创粉丝点击