「一本通 6.2 练习 5」luoguP1445 [Violet]樱花

在这里插入图片描述

analysis

首先先得化简式子,因为这个式子确实看不出来什么
一般看到的题解里有这两种化法:

1.

1 x + 1 y = 1 n ! \frac{1}{x}+\frac{1}{y}=\frac{1}{n!} x1+y1=n!1
x y x + y = n ! \frac{xy}{x+y}=n! x+yxy=n!
x y = n ! ( x + y ) xy=n!(x+y) xy=n!(x+y)
− n ! ( x + y ) + x y = 0 ← → ( n ! x + n ! y ) − x y = 0 -n!(x+y)+xy=0\leftarrow\rightarrow(n!x+n!y)-xy=0 n!(x+y)+xy=0(n!x+n!y)xy=0
对数学敏感的同学相信写到这一步时就已经可以发现一些东西了,这个式子是十字相乘法拆开括号的后两项!


若 设 a = − n ! , b = x , c = y , 则 a ( b + c ) + b c = 0 , 等 式 两 边 同 时 加 上 a 2 若设a=-n!,b=x,c=y,则a(b+c)+bc=0,等式两边同时加上a^2 a=n!,b=x,c=y,a(b+c)+bc=0,a2
则 a 2 + a ( b + c ) + b c = 0 , ( a + c ) ( a + b ) = 0 则a^2+a(b+c)+bc=0,(a+c)(a+b)=0 a2+a(b+c)+bc=0,(a+c)(a+b)=0


于是有 ( x − n ! ) ( y − n ! ) = ( n ! ) 2 (x-n!)(y-n!)=(n!)^2 (xn!)(yn!)=(n!)2
也就是说 ( x − n ! ) ∣ ( n ! ) 2 (x-n!)|(n!)^2 (xn!)(n!)2
那么, ( x − n ! ) (x-n!) (xn!)等价于 ( n ! ) 2 (n!)^2 (n!)2的因子,又由于 ( x − n ! ) (x-n!) (xn!)和x的个数相等,那么x的个数和 ( n ! ) 2 (n!)^2 (n!)2的因子的个数一一对应

2.

设 y = n ! + k 设y=n!+k y=n!+k(至于为什么要这样构造,我也不知道啊)
则原方程为 x ( n ! + k ) = n ! ( x + ( n ! + k ) ) x(n!+k)=n!(x+(n!+k)) x(n!+k)=n!(x+(n!+k))
即 x n ! + x k = n ! ( x + n ! + k ) , x n ! + x k = n ! x + ( n ! ) 2 + n ! k 即xn!+xk=n!(x+n!+k),xn!+xk=n!x+(n!)^2+n!k xn!+xk=n!(x+n!+k),xn!+xk=n!x+(n!)2+n!k
也 就 是 x = ( n ! ) 2 k + n ! 也就是x=\frac{(n!)^2}{k}+n! x=k(n!)2+n!
也 就 是 说 x 的 个 数 等 于 ( n ! ) 2 的 因 子 的 个 数 也就是说x的个数等于(n!)^2的因子的个数 x(n!)2


那么本题就是求 ( n ! ) 2 (n!)^2 (n!)2的因子的个数
求因子个数,根据唯一分解定理的推论:
x = p 1 s 1 × p 2 s 2 × . . . . . . p n s m = ∏ i = 1 m p i s i , p i 是 素 数 x=p_1^{s1}\times p_2^{s_2}\times......p_n^{s_m}=\prod_{i=1}^{m}p_i^{s_i},p_i是素数 x=p1s1×p2s2×......pnsm=i=1mpisi,pi
x 的 约 数 个 数 d ( n ) = ∏ i = 1 m ( s i + 1 ) x的约数个数d(n)=\prod_{i=1}^{m}(s_i+1) xd(n)=i=1m(si+1)根据乘法原理可证明


code

( n ! ) 2 (n!)^2 (n!)2唯一分解的时候,最暴力的做法是直接分解,也就是对每一个n,枚举每个小于 n \sqrt n n 的质数,时间复杂度为 O ( n × n ) O(\sqrt n \times n) O(n ×n),不知为什么居然还在评测机上跑过了

#include<bits/stdc++.h>
using namespace std;
#define loop(i,start,end) for(register int i=start;i<=end;++i)
#define anti_loop(i,start,end) for(register int i=start;i>=end;--i)
#define clean(arry,num) memset(arry,num,sizeof(arry))
#define isdegit(a) ((a>='0'&&a<='9'))
#define ll long long

const ll mod=1e9+7;

template<typename T>void read(T &x){
	x=0;char r=getchar();T neg=1;
	while(!isdegit(r)){if(r=='-')neg=-1;r=getchar();}
	while(isdegit(r)){x=(x<<1)+(x<<3)+r-'0';r=getchar();}
	x*=neg;
}

const int maxn=1e6;
ll prime[maxn],nfp=0;
ll v[maxn];
inline void shai(int n){
	clean(prime,0);
	clean(v,0);
	nfp=0;
	loop(i,2,n){
		if(!v[i]){
			v[i]=i;
			prime[++nfp]=i;
		}
		loop(j,1,nfp){
			if(i*prime[j]>n||v[i]<prime[j])
				break;
			v[prime[j]*i]=prime[j];
		}
	}
}

ll fj[maxn];
inline void fenjie(ll a){
	loop(i,1,nfp){
		if(prime[i]*prime[i]>a) break;
		if(a%prime[i]==0){
			while(a%prime[i]==0){
				a/=prime[i];
				++fj[i];
			}
		}
	}
	if(a==1) return;
	int mark=lower_bound(prime+1,prime+1+nfp,a)-prime;
	++fj[mark];
}

int n;

int main(){
	#ifndef ONLINE_JUDGE
	freopen("datain.txt","r",stdin);
	#endif
	read(n);
	shai(n);
	loop(i,2,n){
		fenjie((ll)i);
	}
	ll res=1;
	for(register int i=1;fj[i]!=0;++i){
		res=(res*(fj[i]<<1|1))%mod;
	}
	printf("%lld\n",res);
	return 0;
}

但是我们不需要那么暴力
可以利用线性筛里面处理出来的数组v(v[i]是i的最小质因子)来加快我们对于每一个数唯一分解的速度
对于一个数,我们可以每次除去它的最小质因子,并且累加记录,然后对于计算后得到的结果继续除其最小质因子,直到把这个数除成1为止
这样的话,时间复杂度降为 O ( n l o g n ) O(nlogn) O(nlogn),这样就可以过了(至于为什么每个数的质因子个数为logn,我不知道)

#include<bits/stdc++.h>
using namespace std;
#define loop(i,start,end) for(register int i=start;i<=end;++i)
#define anti_loop(i,start,end) for(register int i=start;i>=end;--i)
#define clean(arry,num) memset(arry,num,sizeof(arry))
#define isdegit(a) ((a>='0'&&a<='9'))
#define ll long long

const ll mod=1e9+7;

template<typename T>void read(T &x){
	x=0;char r=getchar();T neg=1;
	while(!isdegit(r)){if(r=='-')neg=-1;r=getchar();}
	while(isdegit(r)){x=(x<<1)+(x<<3)+r-'0';r=getchar();}
	x*=neg;
}

const int maxn=1e6;
ll prime[maxn],nfp=0;
ll v[maxn];
inline void shai(int n){
	clean(prime,0);
	clean(v,0);
	nfp=0;
	loop(i,2,n){
		if(!v[i]){
			v[i]=i;
			prime[++nfp]=i;
		}
		loop(j,1,nfp){
			if(i*prime[j]>n||v[i]<prime[j])
				break;
			v[prime[j]*i]=prime[j];
		}
	}
}

ll fj[maxn];
int n;

int main(){
	#ifndef ONLINE_JUDGE
	freopen("datain.txt","r",stdin);
	#endif
	read(n);
	shai(n);
	loop(i,1,n){
		for(int j=i;j!=1;j/=v[j]){
			++fj[v[j]];
			fj[v[j]]%=mod;
		}
	}
	ll res=1;
	for(register int i=1;i<=n;++i){
		res=(res*(fj[i]<<1|1))%mod;
	}
	printf("%lld\n",res);
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AndrewMe8211

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值