【数论,dp】约数国王

链接

https://www.luogu.org/problemnew/show/T25626

大意

l l r之间约数国王的个数

约数国王,指其约数的个数大于比它小的正整数的约数的个数的正整数

思路

50分思路直接欧拉筛
100分思路 dp d p
预处理 1 1 262次方之间所有的约数国王,利用 dp d p 方程,同时多开一倍的数组滚动,要不然会 MLE M L E

50分代码

#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;int l,r,a[100000001],b[500001],ans,now;
int main()
{
    scanf("%d%d",&l,&r);
    a[1]=1;fill(a+2,a+2+r,2);//初始化,每个非1正整数都有两个约数即为1和它本身
    for(int i=2;i<=ceil(sqrt(r));i++)
     for(int j=i;j<=r/i;j++)
      if(i==j) a[i*j]++;else a[i*j]+=2;//一波筛
    for(int i=2;i<=r;i++)
     if(a[i]>now)//存下答案
      {
        if(i>=l) b[++ans]=i;//复制给b
        now=a[i];//保存约数国王
      }
    printf("%d\n",ans);
    for(int i=1;i<=ans;i++) printf("%d ",b[i]);//输出
}

AC代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define N 170000
#define M 100
#define r(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
int min1[N+1],min2[N+1],ans;
bool vis[M+1];
long long a[300],p[M+1],f[N+1],f1[N+1],m[N+1],t;
long long maxn=9223372036854775807LL,x,l,r;
string name,num,name2;
void make1(int i)//滚动数组1
{
       r(j,1<<(i+1),N) f1[j]=maxn;//这也是初始化
        r(j,1<<i,N)
        {
            if(f[j]==maxn) continue;//超出范围不管它
            m[j]=min(m[j],f[j]);//这其实相当于一个初始化了
            double y=log(maxn/f[j])/log(p[i+1]);
            y=min(y,min((double)(N/(j+1)-1),(double)min1[j]));//计算可以转移到的最大值,因为要保证不越出范围所以要用min
            t=1;
            r(k,1,y)
            {
                t*=p[i+1];
                if(f[j]*t<f1[j*k+j])//其实这就是动态转移方程,只不过要两个一起转移
                {
                    f1[j*k+j]=f[j]*t;//动态转移
                    min2[j*k+j]=k;//动态转移
                }
            }
        }
}
void make2(int i)//滚动数组2,以下做法同上,这里就不给出详细解释了
{
       r(j,1<<(i+1),N) f[j]=maxn;
        r(j,1<<i,N)
        {
            if(f1[j]==maxn) continue;
            m[j]=min(m[j],f1[j]);
            double y=log(maxn/f1[j])/log(p[i+1]);
            y=min(y,min((double)(N/(j+1)-1),(double)min2[j]));
            t=1;
            r(k,1,y)
            {
                t*=p[i+1];
                if(f1[j]*t<f[j*k+j])
                {
                    f[j*k+j]=f1[j]*t;
                    min1[j*k+j]=k;
                }
            }
        }
}
int main()
{
    r(i,2,M)//筛素数的个数
    {
        if(!vis[i]) p[++p[0]]=i;
        r(j,1,p[0])
        {
            if(M/p[j]<i) break;
            vis[i*p[j]]=true;
            if(i%p[j]==0) break;
        }
    }
    f[1]=1;
    r(i,2,62) {f[i]=f[i-1]<<1;min1[i]=i-1;}
    r(i,63,N) f[i]=maxn;
    r(i,2,N) m[i]=maxn;//以上初始化
    r(i,1,16)
    if(i&1) make1(i);else make2(i);//动态转移
    r(i,1<<17,N) m[i]=min(m[i],f[i]);//保存答案
    x=maxn;
    for(int i=N;i>1;i--)//因为是求国王,所以要倒过来
    if(m[i]<x)//这是求约数国王
    {
        a[++a[0]]=m[i];//放入数组a
        x=m[i];
    }
    cin>>l>>r;
    r(i,1,a[0]) if(a[i]>=l&&a[i]<=r) ans++;//统计个数
    printf("%d\n",ans);
    for(int i=a[0];i>0;i--) if(a[i]>=l&&a[i]<=r) cout<<a[i]<<' ';//输出
}

后续

此代码也可以 AC A C 洛谷 P1463 P 1463 反素数,只需要在输出的地方改一下就行了,是一个十分高效(搞笑)的算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值