• P1463 [HAOI2007]反素数


    题目描述

    对于任何正整数x,其约数的个数记作g(x)。例如g(1)=1、g(6)=4。

    如果某个正整数x满足:g(x)>g(i) 0<i<x,则称x为反质数。例如,整数1,2,4,6等都是反质数。

    现在给定一个数N,你能求出不超过N的最大的反质数么?

    输入输出格式

    输入格式:

    一个数N(1<=N<=2,000,000,000)。

    输出格式:

    不超过N的最大的反质数。

    输入输出样例

    输入样例#1: 
    1000
    输出样例#1: 
    840

    Solution:

      本题思路实在是巧妙~~!(膜拜比我小的巨佬——老$K$,他的思路让我受益匪浅!)

      第一种思路(其实,准确说是第二种,是先搞懂了搜索的思路才自己琢磨的打表):

      首先我们讲下暴力打表,由于数据$nleq 2*10^9$,于是我们可以线下打表,直接暴力模拟,先根据题目可知$1,2,4,6$都是反质数,所以先输出这$4$个数,记当前最大约数个数$mx$为$4$(即$6$的约数个数),然后从$7$枚举到$2*10^9$,每次判断约数个数是否大于$mx$,大于就更新$mx$并输出该数。表搞完后,其实也没多少个,用数组存下来,然后二分答案找第一个比$n$小的就$OK$了(我打表的代码没有输出,也许机房配置太好了很神奇~~)。

      贴一下我的暴力打表程序,显然直接打$1$到$2*10^9$的表肯定行不通,我们先打从$1$到$1000000$的来找规律:

    #include<bits/stdc++.h>
    #define il inline
    #define ll long long
    #define For(i,a,b) for(ll (i)=(a);(i)<=(b);(i)++)
    #define Max(a,b) ((a)>(b)?(a):(b))
    using namespace std;
    ll p[200]={0,1,2,4,6},mx=4;
    il bool get(ll x){
        ll ans=2;
        for(ll i=2;i*i<=x;i++){
            if(x%i==0&&x/i!=i)ans+=2;
            else if(x%i==0&&x/i==i)ans++;
        }
        //cout<<ans<<endl;
        if(mx<ans){
            mx=ans;
            return 1;
        }
        return 0;
    }
    int main(){
        freopen("la.out","w",stdout);
        For(i,1,1000000)if(get(i)){printf("%lld,",i);}
        return 0;
    }

      然后对打出来的数:

      $1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,1260,1680,2520,5040,7560,10080,15120,20160,25200,27720,45360,50400,55440,83160,83160,110880,166320,221760,277200,332640,498960,554400,665280,720720$

      进行质因数分解:$$1=1$$
    $$2=2^1$$
    $$4=2^2$$
    $$6=2^1*3^1$$
    $$12=2^2*3^1$$
    $$24=2^3*3^1$$
    $$36=2^2*3^2$$
    $$48=2^4*3^1$$
    $$60=2^2*3^1*5^1$$
    $$120=2^3*3^1*5^1$$
    $$180=2^2*3^2*5^1$$
    $$240=2^4*3^1*5^1$$
    $$360=2^3*3^2*5^1$$
    $$720=2^4*3^2*5^1$$
    $$840=2^3*3^1*5^1*7^1$$
    $$1260=2^2*3^2*5^1*7^1$$
    $$1680=2^4*3^1*5^1*7^1$$
    $$2520=2^3*3^2*5^1*7^1$$
    $$5040=2^4*3^2*5^1*7^1$$
    $$7560=2^3*3^3*5^1*7^1$$
    $$10080=2^5*3^2*5^1*7^1$$
    $$15120=2^4*3^3*5^1*7^1$$
    $$20160=2^6*3^2*5^1*7^1$$
    $$25200=2^4*3^2*5^2*7^1$$
    $$27720=2^3*3^2*5^1*7^1*11^1$$
    $$45360=2^4*3^4*5^1*7^1$$
    $$50400=2^5*3^2*5^2*7^1$$
    $$55440=2^4*3^2*5^1*7^1*11^1$$
    $$83160=2^3*3^3*5^1*7^1*11^1$$
    $$83160=2^3*3^3*5^1*7^1*11^1$$
    $$110880=2^5*3^2*5^1*7^1*11^1$$
    $$166320=2^4*3^3*5^1*7^1*11^1$$
    $$221760=2^6*3^2*5^1*7^1*11^1$$
    $$277200=2^4*3^2*5^2*7^1*11^1$$
    $$332640=2^5*3^3*5^1*7^1*11^1$$
    $$498960=2^4*3^4*5^1*7^1*11^1$$
    $$554400=2^5*3^2*5^2*7^1*11^1$$
    $$665280=2^6*3^3*5^1*7^1*11^1$$
    $$720720=2^4*3^2*5^1*7^1*11^1*13^1$$

     容易发现将质数从小到大排,指数会单调不上升(其实后面老$K$的思路用到了这个性质),那么根据这个东西我们可以线下贪心打表前$12$个质数的幂累乘的形式(幂单调不上升),就能处理出所有的$leq 2*10^9$的反质数。然后将打表出的数据用数组存下,查询时直接模拟就好了。(打表的代码和下面的搜索代码其实类似,我们可以依次枚举指数保证不上升,然后累乘出的数就是反质数,比较简单就不贴代码了)

       贴一个最后的反质数表

    #include<bits/stdc++.h>
    using namespace std;
    int p[500]={1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,1260,1680,2520,5040,7560,10080,15120,20160,25200,27720,45360,50400,55440,83160,110880,166320,221760,277200,332640,498960,554400,665280,720720,1081080,1441440,2162160,2882880,3603600,4324320,6486480,7207200,8648640,10810800,14414400,17297280,21621600,32432400,36756720,43243200,61261200,73513440,110270160,122522400,147026880,183783600,245044800,294053760,367567200,551350800,698377680,735134400,1102701600,1396755360,2001000000};
    int main()
    {
        int n;
        cin>>n;
        int ans;
        for(int i=0;;i++)
        {
            if(p[i]>n) {
                cout<<p[i-1]<<endl;
                return 0;
            }
        }
    }

      然后讲讲第二种炒鸡玩美的思路:

      1、对于一个数$x$,我们根据唯一分解定理,可以将$x$分解为质数的幂相乘的形式:$x=prod{p_i^{k_i}}$,其中$p_i$为质数。

      2、将数$x$唯一分解表示后,则$x$的约数个数$=prod{(k_i+1)}$(这个证明很简单,$x$的因子是$x$将唯一分解后的质数改变指数相乘得来,质数$p_i$的指数从$0$到$k_i$共$k_i+1$种情况,所以总的约数个数为$k_i+1$的累乘)。

      3、我们不妨将$p_i$从小到大排列(即保证$p_i>p_{i-1}$),那么不难发现当$d<f$且$k_d<k_f$时一定不是反质数,证明很简单:

      $ecause x=prod{p_i^{k_i}}$且$p_i$单调递增

      又$ecause d<f,; k_d<k_f$,$x$的约数个数$sum_x=prod{(k_i+1)}$

      $ herefore $一定可以构造一个数$y$,使得$sum_y=sum_x$,只需交换$p_d,p_f$的指数$k_d,k_f$即可

      又$ecause p_i$单调递增

      $ herefore sum_y<sum_x$,那么$g(x)=g(y)$,而$x>y$,显然$x$不可能为一个反质数。(举栗:$2*3^3<2^3*3$,而它们约数个数均为$8$个,显然$2*3^3$不可能是反质数)

      由上面的证明我们得到一个反质数$q$唯一分解后将$p_i$递增排列,对应的指数$k_i$一定单调不上升。

      因为前$12$个素数的乘积已经爆$2*10^9$,于是我们处理出前$12$个素数,然后$dfs$搜索枚举各质数的指数,判断$ans$即可。

      (具体实现看代码,$ORZ——LK$,被低两个年级的人吊打,好虚啊~)

    代码:

    #include<bits/stdc++.h>
    #define il inline
    #define ll long long
    #define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
    using namespace std;
    const int prime[15]={0,2,3,5,7,11,13,17,19,23,29,31,37};
    ll n,ans,mx;
    il void dfs(int nu,int qnu,ll t,ll tot){
        if(t>mx||t==mx&&tot<ans)ans=tot,mx=t; //当约数个数t大于当前最大约数个数或者约数个数相等且这个数小于当前答案时,更新答案和约数个数最大值
        ll sum=tot,i=0,nt=0;
        while(i<qnu){
            i++;       //枚举约数个数
            if(prime[nu]>n/sum)break;  //可行性剪枝,注意直接prime[nu]*sum会爆long long
            nt=(i+1)*t,tot*=prime[nu]; //nt为新的约数个数,tot为新的数值
            if(tot<=n)dfs(nu+1,i,nt,tot); //继续递归搜索
        }
    }
    /*nu为当前的素数下标,qnu为指数,t为当前约数个数,tot为当前的值*/
    int main(){
        cin>>n;
        dfs(1,30,1,1);  //2^30>=1e9
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    GMA Round 1 数列求单项
    GMA Round 1 双曲线与面积
    多线程环境中安全使用集合API(含代码)
    使用synchronized获取互斥锁的几点说明
    ThreadPoolExecutor线程池
    线程状态转换
    volatile关键字
    守护线程与线程阻塞的四种情况
    线程挂起,恢复与终止
    线程中断
  • 原文地址:https://www.cnblogs.com/five20/p/9085646.html
Copyright © 2020-2023  润新知