**
解题思路
**
在做这题的时候被卡住了,不知道该怎么做下去。看了一下别人的题解,发现解题的关键在于我们要利用x,y是整数的条件。
首先研究一下x,y,因为
1
x
+
1
y
=
1
n
!
\frac{1}{x}+\frac{1}{y}=\frac{1}{n!}
x1+y1=n!1所以x,y必然大于n!
看到别人将y设为n!+a的时候我是惊呆的,为什么能想到这样子替换呢?作为蒟蒻我是想不到的。既然是蒟蒻我就只对式子做一些简单的变换吧。
我们可以将原式通分移项整理后得到
x
=
y
n
!
y
−
n
!
x=\frac{yn!}{y-n!}
x=y−n!yn!,这样子我们就可以不需要关注x了,只需要关注y,因为我们只需要调节y的值使得x是整数那么这一对(x,y)就是合法的解了。但是这个式子上下都有y,太难受了!参数分离!于是
x
=
y
n
!
y
−
n
!
=
(
y
−
n
!
)
n
!
+
(
n
!
)
2
y
−
n
!
=
n
!
+
(
n
!
)
2
y
−
n
!
x=\frac{yn!}{y-n!}=\frac{(y-n!)n!+(n!)^{2}}{y-n!}=n!+\frac{(n!)^{2}}{y-n!}
x=y−n!yn!=y−n!(y−n!)n!+(n!)2=n!+y−n!(n!)2,因为x是整数,n!也是整数,所以
(
n
!
)
2
y
−
n
!
\frac{(n!)^{2}}{y-n!}
y−n!(n!)2这一坨必须是整数,所以
y
−
n
!
y-n!
y−n!必然整除
(
n
!
)
2
(n!)^{2}
(n!)2,所以
y
−
n
!
y-n!
y−n!的取值个数为
(
n
!
)
2
(n!)^{2}
(n!)2的因子个数,可以看出,这也就是
y
y
y的取值个数。因此我们只需要对
(
n
!
)
2
(n!)^{2}
(n!)2进行质因数分解再根据约数个数等于
∏
i
=
1
x
(
c
i
+
1
)
\prod_{i=1}^{x}(c_{i}+1)
i=1∏x(ci+1),即可得解。其中
x
x
x为不同质因数的个数,
c
i
c_i
ci为第i个质数的含有的个数。
分割线
一点总结:
错误点:1.质因数分解时,当当前素数的平方已经大于被分解数a,则停止循环,否则复杂度就不是
a
\sqrt{a}
a。
改进点1:利用欧拉筛记录下的v[i](即每一个的最小质因子)信息加速质因数分解,设当前数为a,则cnt[v[a]]++,a变为a/v[a],重复此操作直至a变为1,复杂度为O(a的因子个数)。【PS:这操作实在是太骚了】
改进点2:对n!做分解质因数有另外更加快速的算法,参见Slager_Z的博客。(速度比上一个改进方法还快)
加速算法的复杂度
这位大佬给出了更快的算法,但没有给出该算法的复杂度,在此粗略地计算一下(本人是数学渣)。
要阅读以下推导请先理解Slager Z的博客内容!
假设对n!这个数进行质因数分解时用到了q个质数,分别记作
p
1
,
p
2
.
.
.
p
q
{p}_{1},{p}_{2}...{p}_{q}
p1,p2...pq,则最后一个数
p
q
{p}_{q}
pq为最后一个小于n的质数。
对于每个素数
p
i
{p}_{i}
pi我们要用
p
1
i
,
p
i
2
.
.
.
p
i
x
i
{p}_{1}^{i},{p}_{i}^{2}...{p}_{i}^{x_{i}}
p1i,pi2...pixi这
x
i
x_{i}
xi个数去计算对n!质因数分解结果的贡献,每次计算时间是
O
(
1
)
O(1)
O(1)的,因此复杂度为
O
(
x
i
)
O(x_{i})
O(xi)。
显然最后一个数
p
i
x
{p}_{i}^{x}
pix小于等于n,而
p
i
x
+
1
{p}_{i}^{x+1}
pix+1大于n,故
x
i
=
l
o
g
p
i
n
x_{i}=log_{p_{i}}n
xi=logpin
总复杂度为
∑
i
=
1
q
l
o
g
p
i
n
\sum_{i=1}^{q}log_{p_{i}}n
i=1∑qlogpin
我们进行放大(求上界),将所有的
p
i
p_{i}
pi缩小到
p
1
=
2
p_{1}=2
p1=2,那就是把原式放大到了
∑
i
=
1
q
l
o
g
2
n
\sum_{i=1}^{q}log_{2}n
i=1∑qlog2n
即复杂度
Θ
(
q
l
o
g
2
n
)
\Theta(qlog_{2}n)
Θ(qlog2n)
又根据素数定理
q
≈
x
l
n
x
q≈\frac{x}{lnx}
q≈lnxx
那我们再四舍五入一下【把底数2变成e】就变成
Θ
(
n
)
\Theta(n)
Θ(n)
因为我们是放大过的了,所以复杂度显然小于
O
(
n
)
O(n)
O(n),但是一定也是会大于
O
(
n
)
O(\sqrt{n})
O(n)的,毕竟对n分解就是这个复杂度了,对n!分解肯定要更大一些。
觉得写得好的话,就点个赞让我知道一下呗~
改进前:共1100ms±
#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
using namespace std;
typedef unsigned long long ull;
const int inf=1e9+10,mod=1e9+7,N=1e6+100;
ull n,ans,cnt[N],prime[N],v[N],len;
inline void Euler(){
rep(i,2,n){
if(v[i]==0){
v[i]=i;
prime[++len]=i;
}
rep(j,1,len){
if((prime[j]>v[i])||(i*prime[j]>n)) break;
v[i*prime[j]]=prime[j];
}
}
}
inline void split(int a){
rep(i,1,len){
if(prime[i]*prime[i]>a) break;//当大于等于根号n的时候就退出,复杂度才是根号n的。
while(a%prime[i]==0) cnt[prime[i]]++,a/=prime[i];
}
if(a>1) cnt[a]++;
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
cin>>n;
if(n==1){
cout<<1;
return 0;
}
Euler();
rep(i,1,n) split(i);
ans=1;
rep(i,1,n) ans=(ans*(cnt[i]*2+1))%mod;
cout<<ans;
return 0;
}
//当n=1的时候要特判!,唯一一个无法进行质因数分解的数.
//对n!做质因数分解复杂度一定是O(n根号n)吗
改进后:(运用上述上界O(n)的算法)共90ms±。
#include<bits/stdc++.h>
#include<windows.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
using namespace std;
typedef unsigned long long ull;
const int inf=1e9+10,mod=1e9+7,N=2e7+100;
ull n,ans,cnt[N],prime[N],v[N],len;
inline void Euler(){
rep(i,2,n){
if(v[i]==0){
v[i]=i;
prime[++len]=i;
}
rep(j,1,len){
if((prime[j]>v[i])||(i*prime[j]>n)) break;
v[i*prime[j]]=prime[j];
}
}
}
int main(){
freopen("input.in","r",stdin); freopen("output.out","w",stdout);
unsigned int start_time=GetTickCount();
ios::sync_with_stdio(false); cin.tie(0);
cin>>n;
if(n==1){
cout<<1;
return 0;
}
Euler();
rep(i,1,len){
ull j=prime[i];
while(j<=n){
cnt[i]+=(ull)n/j;
j=j*prime[i];
}
}
ans=1;
rep(i,1,len) ans=(ans*(cnt[i]*2+1))%mod;
cout<<ans;
cout<<"time="<<GetTickCount()-start_time<<"ms";
return 0;
}