数论-质数-樱花BZOJ2721

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/TengWan_Alunl/article/details/83305757

**

解题思路

**
在做这题的时候被卡住了,不知道该怎么做下去。看了一下别人的题解,发现解题的关键在于我们要利用x,y是整数的条件
首先研究一下x,y,因为 1 x + 1 y = 1 n ! \frac{1}{x}+\frac{1}{y}=\frac{1}{n!} 所以x,y必然大于n!
看到别人将y设为n!+a的时候我是惊呆的,为什么能想到这样子替换呢?作为蒟蒻我是想不到的。既然是蒟蒻我们就对式子做一些简单的变换吧。
我们可以将原式通分移项整理后得到 x = y n ! y n ! x=\frac{yn!}{y-n!} ,这样子我们就可以不需要关注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 ! y-n! 必然整除 ( n ! ) 2 (n!)^{2} 。因为 y n ! y-n! 整除 ( n ! ) 2 (n!)^{2} ,所以 y n ! y-n! 的取值个数为 ( n ! ) 2 (n!)^{2} 的因子个数,可以看出,这也就是 y y 的取值个数。因此我们只需要对 ( n ! ) 2 (n!)^{2} 进行质因数分解再根据约数个数= Π i = 1 x ( c i + 1 ) \Pi_{i=1}^{x}(c^{i}+1) ,x为不同的质因数个数,ci为含有的第i个质数的个数。


分割线


一点总结:
错误点:1.质因数分解时,当当前素数的平方已经大于被分解数a,则停止循环,否则复杂度就不是 a \sqrt{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} ,则最后一个数 p q {p}_{q} 为最后一个小于n的质数。
对于每个素数 p i {p}_{i} 我们要用 p 1 i , p i 2 . . . p i x i {p}_{1}^{i},{p}_{i}^{2}...{p}_{i}^{x_{i}} x i x_{i} 个数去计算对n!质因数分解结果的贡献,每次计算时间是 O ( 1 ) O(1) 的,因此复杂度为 O ( x i ) O(x_{i})
显然最后一个数 p i x {p}_{i}^{x} 小于等于n,而 p i x + 1 {p}_{i}^{x+1} 大于n,故 x i = l o g p i n x_{i}=log_{p_{i}}n
总复杂度为 Σ i = 1 q l o g p i n \Sigma_{i=1}^{q}log_{p_{i}}n
我们进行放大(求上界),将所有的 p i p_{i} 缩小到 p 1 = 2 p_{1}=2 ,那就是把原式放大到了 Σ i = 1 q l o g 2 n \Sigma_{i=1}^{q}log_{2}n
即复杂度 Θ ( q l o g 2 n ) \Theta(qlog_{2}n)
q显然小于n。
推完了。
显然是一个比较松的上界,实际应该比这个小一些(比如把所有的 p i p_{i} 缩小到 n 2 \frac{n}{2} 什么鬼的?)。。欢迎巨佬来踩。
改进前:共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;
}

猜你喜欢

转载自blog.csdn.net/TengWan_Alunl/article/details/83305757