数论-质数-樱花BZOJ2721

**

解题思路

**
在做这题的时候被卡住了,不知道该怎么做下去。看了一下别人的题解,发现解题的关键在于我们要利用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=yn!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=yn!yn!=yn!(yn!)n!+(n!)2=n!+yn!(n!)2因为x是整数,n!也是整数,所以 ( n ! ) 2 y − n ! \frac{(n!)^{2}}{y-n!} yn!(n!)2这一坨必须是整数,所以 y − n ! y-n! yn!必然整除 ( n ! ) 2 (n!)^{2} (n!)2,所以 y − n ! y-n! yn!的取值个数为 ( 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=1x(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=1qlogpin
我们进行放大(求上界),将所有的 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=1qlog2n
即复杂度 Θ ( q l o g 2 n ) \Theta(qlog_{2}n) Θ(qlog2n)
又根据素数定理 q ≈ x l n x q≈\frac{x}{lnx} qlnxx
那我们再四舍五入一下【把底数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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值