**
解题思路
**
在做这题的时候被卡住了,不知道该怎么做下去。看了一下别人的题解,发现解题的关键在于我们要利用x,y是整数的条件。
首先研究一下x,y,因为
所以x,y必然大于n!
看到别人将y设为n!+a的时候我是惊呆的,为什么能想到这样子替换呢?作为蒟蒻我是想不到的。既然是蒟蒻我们就对式子做一些简单的变换吧。
我们可以将原式通分移项整理后得到
,这样子我们就可以不需要关注x了,只需要关注y,因为我们只需要调节y的值使得x是整数那么这一对(x,y)就是合法的解了。但是这个式子上下都有y,太难受了!参数分离!于是
,因为x是整数,所以
必然整除
。因为
整除
,所以
的取值个数为
的因子个数,可以看出,这也就是
的取值个数。因此我们只需要对
进行质因数分解再根据约数个数=
,x为不同的质因数个数,ci为含有的第i个质数的个数。
分割线
一点总结:
错误点:1.质因数分解时,当当前素数的平方已经大于被分解数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个质数,分别记作
,则最后一个数
为最后一个小于n的质数。
对于每个素数
我们要用
这
个数去计算对n!质因数分解结果的贡献,每次计算时间是
的,因此复杂度为
。
显然最后一个数
小于等于n,而
大于n,故
总复杂度为
我们进行放大(求上界),将所有的
缩小到
,那就是把原式放大到了
即复杂度
q显然小于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)吗
改进后:(运用上述上界nlogn的算法)共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;
}