Description
数学的王国里,有一些约数国王……约数国王的定义是这样的:一个大于 1 1 1的整数 n n n,如果它约数的个数比 1 1 1~ n − 1 n-1 n−1的每个整数的约数的个数都要多,那么我们就称它为约数国王。聪明的小明在奥数书上认识了它们,于是产生了一个问题:他想知道L到R之间一共有多少个约数国王?它们分别又是谁?
Input
输入文件只有一行,包含一个 l l l,一个 r r r,表示小明想知道的范围。
Output
只有一行,第一个数 h h h,表示 l l l~ r r r内一共有多少个约数国王,接下来 h h h个从小到大的数(为了防止国王们打架,你需要按顺序输出),表示约数国王分别是谁。
Data Constraint
对于
30
30%
30的数据,
1
≤
l
≤
r
≤
200000
1\leq l\leq r\leq 200000
1≤l≤r≤200000。
对于
50
50%
50的数据,
1
≤
l
≤
r
≤
500000
1\leq l\leq r\leq500000
1≤l≤r≤500000。
对于
70
70%
70的数据,保证最大的约数国王的约数的个数不大于
1000
1000
1000。
对于
100
100%
100的数据,
1
≤
l
≤
r
1\leq l\leq r
1≤l≤r, 并且保证
l
l
l,
r
r
r在
64
64
64位整型以内,最大的约数国王的约数的个数不大于
200000
200000
200000。
Solutions
① ① ① 30 30 30分
直接暴力就好了,枚举时注意不要枚举所有因数,只需要枚举两个因数中较小的那个因数,就可以得出答案。
注意完全平方数的特殊情况。
时间复杂度
O
(
n
n
)
O(n\sqrt n)
O(nn)
Code
#include<cstdio>
#include<cmath>
#define fo(i,x,y) for(int i=x;i<=y;i++)
#define p 200000
using namespace std;
long long a[200],l,r,t,f,max;
int main()
{
freopen("king.in","r",stdin);
freopen("king.out","w",stdout);
fo(i,2,p)
{
f=0;
fo(j,1,sqrt(i*1.0)) if(i%j==0)f+=2;
if(floor(sqrt(i*1.0))==sqrt(i*1.0))f--;
if(f>max){max=f;a[++a[0]]=i;}
}
scanf("%lld%lld",&l,&r);
fo(i,1,a[0])if((a[i]>=l)&&(a[i]<=r))t++;printf("%lld",t);
fo(i,1,a[0])if((a[i]>=l)&&(a[i]<=r))printf(" %lld",a[i]);
}
② ② ② 50 50 50分
根据乘法原理,设
k
=
p
1
q
1
∗
p
2
q
2
∗
p
3
q
3
…
p
n
q
n
k=p1^{q1}*p2^{q2}*p3^{q3}…pn^{qn}
k=p1q1∗p2q2∗p3q3…pnqn(
p
i
p_i
pi是
k
k
k的质因数,
q
i
q_i
qi是
p
i
p_i
pi对应的指数),则
k
k
k的约数的个数为
(
q
1
+
1
)
(
q
2
+
1
)
(
q
3
+
1
)
…
(
q
n
+
1
)
(q1+1)(q2+1)(q3+1)…(qn+1)
(q1+1)(q2+1)(q3+1)…(qn+1)。
让我们举个栗子:
假设有一数
36
=
2
2
∗
3
2
36=2^2*3^2
36=22∗32
那么其实
2
0
,
1
,
2
∗
3
0
,
1
,
2
2^{0,1,2}*3^{0,1,2}
20,1,2∗30,1,2都是
36
36
36的因数,请读者自行思索
因为可以是
0
0
0,所以需要把各指数加一,然后乘起来,就是约数的个数了。
那么,通过枚举
k
k
k的质因数和指数,也可以求出
k
k
k的约数的个数。注意同样是枚举小于等于根号
k
k
k的质因数,不要枚举全部。
设
P
0
P_0
P0表示根号
n
n
n以内质数的个数,则时间复杂度
O
(
n
∗
P
0
)
O(n*P_0)
O(n∗P0)
Code
#include<cstdio>
#include<cmath>
#define fo(i,x,y) for(int i=x;i<=y;i++)
using namespace std;
const int max=500000,po=sqrt(max*1.0);
bool bz[po+1];
int f[max+1],p[169],a[200],mx,l,r,t;
int main()
{
freopen("king.in","r",stdin);
freopen("king.out","w",stdout);
fo(i,2,po)
{
if (!bz[i]) p[++p[0]]=i;
fo(j,1,p[0])
{
if(max/p[j]<i) break;
bz[i*p[j]]=1;
if (i%p[j]==0) break;
}
}
fo(i,1,max)
{
f[i]=1; int k=i;
fo(j,1,p[0])
{
if (p[j]>sqrt(i*1.0)) break;
if (k==1) break;
int a=0;
while (k%p[j]==0)
{
a++;k/=p[j];
}
f[i]*=a+1;
}
if (k>1) f[i]*=2;
if(f[i]>mx){mx=f[i];a[++a[0]]=i;}
}
scanf("%lld%lld",&l,&r);
fo(i,1,a[0])if((a[i]>=l)&&(a[i]<=r))t++;printf("%lld",t);
fo(i,1,a[0])if((a[i]>=l)&&(a[i]<=r))printf(" %lld",a[i]);
}
③ ③ ③ F u l l Full Full S o r e ( 100 ) Sore(100) Sore(100)
首先,我们设一个 c [ i ] c[i] c[i],表示因数个数(包括本数)为 i i i,的最小正整数,那么对于一个数 c [ i ] c[i] c[i],如果 c [ i ] < c[i]< c[i]<任何一个 c [ i + k ] c[i+k] c[i+k] ( k k k为正整数),那么 c [ i ] c[i] c[i]就是一个约数国王。
对于求出 c [ i ] c[i] c[i],我们可以设 P [ i ] P[i] P[i]为从小到大的质数中的第 i i i个。这里用线性筛法可以求出来。
我们可以在设一个F[i,j]表示,有i个不同质因数构成,有j个因数的最小正整数。
那么,
c
[
i
]
=
M
a
x
F
[
k
,
i
]
c[i]=Max{F[k,i]}
c[i]=MaxF[k,i]。
求一个数的因子个数的公式:
N
=
P
1
A
1
+
P
2
A
2
+
P
3
A
3
+
.
.
.
+
P
m
A
m
N=P1^{A_1}+P2 ^ {A_2}+P3^{A_3}+...+Pm^{A_m}
N=P1A1+P2A2+P3A3+...+PmAm也就是
N
=
N =
N=
∑
i
=
1
m
P
i
A
i
\sum\limits^{m}_{i = 1}Pi^{Ai}
i=1∑mPiAi (这里所有的
P
P
P都是质数),那么,
N
N
N的因数个数为
(
A
1
+
1
)
(
A
2
+
1
)
(
A
3
+
1
)
…
(
A
m
+
1
)
(A1+1)(A2+1)(A3+1)…(Am+1)
(A1+1)(A2+1)(A3+1)…(Am+1)。
证明:
若 M M M是 N N N的因数, M = P 1 K 1 + P 2 K 2 + P 3 K 3 + . . . + P m K m M=P1^{K1}+P2^{K2}+P3^{K3}+...+Pm^{Km} M=P1K1+P2K2+P3K3+...+PmKm也就是 M = ∑ i = 1 m P i K i ( K i < = A i ) M=\sum\limits^{m}_{i = 1}Pi^{Ki}(Ki<=Ai) M=i=1∑mPiKi(Ki<=Ai) ,那么,只要 K i Ki Ki选 0 0 0~ A i Ai Ai, M ∣ N M|N M∣N,根据乘法原理, N N N的因数个数 ∏ i = 1 m ( A i + 1 ) ∏\limits^{m}_{i=1}(Ai + 1) i=1∏m(Ai+1)也就是 ( A 1 + 1 ) ( A 2 + 1 ) ( A 3 + 1 ) . . . ( A m + 1 ) (A1+1)(A2+1)(A3+1)...(Am+1) (A1+1)(A2+1)(A3+1)...(Am+1)
那么F的转移方程如此:
F
[
i
+
1
,
j
∗
(
k
+
1
)
]
=
m
i
n
(
F
[
i
,
j
]
∗
P
[
i
+
1
]
k
)
F[i+1,j∗(k+1)]=min(F[i,j]∗P[i+1]^k)
F[i+1,j∗(k+1)]=min(F[i,j]∗P[i+1]k)
为什么要乘以 P [ i + 1 ] k P[i+1]^k P[i+1]k呢,你想想,选i+1个素数,当然是选前i个最优。
那我们来讨论一下j的取值范围,可想到 2 i < = j < = m x 2^i<=j<=mx 2i<=j<=mx, m x mx mx为数组开的下限,为什么是 2 2 2的 i i i次方呢,我们在选第 i + 1 i+1 i+1个素数时,前面已经选了 i i i个了,每一个素数最少选 1 1 1个,就会有 2 i 2^i 2i个因子,故如此。
那么代码就很容(kun)易(nan)想象出来了
Code:
空间复杂度是 O ( n log n ) O(n\log n) O(nlogn),不算很大。但是我们打个滚动数组会更好,时间也会小一些。
# include<cstdio>
# include<cmath>
# define fo(i,x,y) for(int i=x;i<=y;i++)
const int am=170000,an=17,up=100;
int min[am+1],min1[am+1];
bool bz[up+1];
long long a[300],p[up+1],f[am+1],f1[am+1],m[am+1];
long long max=9223372036854775807LL;
int main()
{
freopen("king.in","r",stdin);
freopen("king.out","w",stdout);
fo(i,2,up)
{
if (!bz[i]) p[++p[0]]=i;
fo(j,1,p[0])
{
if (up/p[j]<i)
break;
bz[i*p[j]]=1;
if (i%p[j]==0)
break;
}
}
f[1]=1;
fo(i,2,62)
{
f[i]=f[i-1]*2;
min[i]=i-1;
}
fo(i,63,am)f[i]=max;
fo(i,2,am) m[i]=max;
fo(i,1,16)
if (i%2)
{
fo(j,1<<(i+1),am) f1[j]=max;
fo(j,1<<i,am)
{
if (f[j]==max) continue;
if (f[j]<m[j]) m[j]=f[j];
double y=log(max/f[j])/log(p[i+1]);
if(am/(j+1)-1<y) y=am/(j+1)-1;
if(min[j]<y) y=min[j];
long long op=1;
fo(k,1,y)
{
op*=p[i+1];
if(f[j]*op<f1[j*k+j])
{
f1[j*k+j]=f[j]*op;
min1[j*k+j]=k;
}
}
}
}
else
{
fo(j,1<<(i+1),am) f[j]=max;
fo(j,1<<i,am)
{
if (f1[j]==max) continue;
if (f1[j]<m[j]) m[j]=f1[j];
double y=log(max/f1[j])/log(p[i+1]);
if(am/(j+1)-1<y) y=am/(j+1)-1;
if(min1[j]<y) y=min1[j];
long long op=1;
fo(k,1,y)
{
op*=p[i+1];
if(f1[j]*op<f[j*k+j])
{
f[j*k+j]=f1[j]*op;
min[j*k+j]=k;
}
}
}
}
fo(i,1<<17,am)
if(f[i]<m[i])
m[i]=f[i];
long long x=max;
for(int i=am; i>1; i--)
if(m[i]<x)
{
a[++a[0]]=m[i];
x=m[i];
}
long long l,r;
scanf("%lld %lld",&l,&r);
int y=0;
fo(i,1,a[0])
if ((a[i]>=l)&&(a[i]<=r))
y++;
printf("%d",y);
for(int i=a[0]; i>=1; i--)
if ((a[i]>=l)&&(a[i]<=r))
printf(" %lld",a[i]);
}