链接
https://www.luogu.org/problemnew/show/T25626
大意
求 l l 到之间约数国王的个数
约数国王,指其约数的个数大于比它小的正整数的约数的个数的正整数
思路
50分思路直接欧拉筛
100分思路
dp
d
p
预处理
1
1
到次方之间所有的约数国王,利用
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
反素数,只需要在输出的地方改一下就行了,是一个十分高效(搞笑)的算法