樱花(线性筛+唯一分解定理)
题目链接:https://ac.nowcoder.com/acm/problem/50557(https://ac.nowcoder.com/acm/problem/15428)
题目来源:牛客网
Problem Description
求不定方程:
1
x
+
1
y
=
1
n
!
\frac{1}{x}+\frac{1}{y}=\frac{1}{n!}
x1+y1=n!1
的正整数解(x,y)的数目。
Input
一个整数n。
Output
一个整数,表示有多少对(x,y)满足题意。答案对 1 0 9 10^9 109+7取模。
Sample Input
2
Sample Output
3
Hint
共有三个数对(x,y)满足条件,分别是(3,6),(4,4)和(6,3)。
Remark
对于30%的数据,n≤100;
对于全部数据,1≤n≤ 1 0 6 10^6 106 。
Solution
这题很显然是数论题,下面我们来进行数学推导:
![](https://img-blog.csdnimg.cn/20200830082612437.jpg#pic_center)
因为
1
x
+
1
y
=
1
n
!
(
1
≤
n
≤
1
0
6
)
\frac{1}{x}+\frac{1}{y}=\frac{1}{n!} (1≤n≤10^6)
x1+y1=n!1(1≤n≤106)
所以
1
x
、
1
y
<
1
n
!
,
即
x
、
y
>
n
!
\frac{1}{x}、\frac{1}{y}<\frac{1}{n!} ,即x、y>n!
x1、y1<n!1,即x、y>n!
由于
x
、
y
>
n
!
x、y>n!
x、y>n!,我们不妨令
y
=
n
!
+
k
(
k
∈
N
∗
)
y=n!+k(k\in{N*})
y=n!+k(k∈N∗)
我们将
y
=
n
!
+
k
(
k
∈
N
∗
)
y=n!+k(k\in{N*})
y=n!+k(k∈N∗)代入
1
x
+
1
y
=
1
n
!
\frac{1}{x}+\frac{1}{y}=\frac{1}{n!}
x1+y1=n!1中,得
1
x
+
1
n
!
+
k
=
1
n
!
\frac{1}{x}+\frac{1}{n!+k}=\frac{1}{n!}
x1+n!+k1=n!1
左右通分,得
n
!
+
k
+
x
x
×
(
n
!
+
k
)
=
1
n
!
\frac{n!+k+x}{x\times(n!+k)}=\frac{1}{n!}
x×(n!+k)n!+k+x=n!1
分子分母交叉相乘,化简得
n
!
×
(
n
!
+
k
)
=
k
×
x
n!\times(n!+k)=k\times x
n!×(n!+k)=k×x
将
x
x
x提出,化简得
x
=
(
n
!
)
2
k
+
n
!
x=\frac{(n!)^2}{k}+n!
x=k(n!)2+n!
至此,变形完毕,关键公式已经得到。我们已经有了
x
=
(
n
!
)
2
k
+
n
!
x=\frac{(n!)^2}{k}+n!
x=k(n!)2+n!这条关键公式,我们已经有了
x
=
(
n
!
)
2
k
+
n
!
x=\frac{(n!)^2}{k}+n!
x=k(n!)2+n!这条关键公式,我们已经有了
x
=
(
n
!
)
2
k
+
n
!
x=\frac{(n!)^2}{k}+n!
x=k(n!)2+n!这条关键公式,因为这个公式太重要了,所以重要的事情说三遍,嘿嘿。
![](https://img-blog.csdnimg.cn/20200830085137212.jpg#pic_center)
因为
x
x
x是正整数,所以
(
n
!
)
2
k
+
n
!
\frac{(n!)^2}{k}+n!
k(n!)2+n!也要是正整数。这就意味着
(
n
!
)
2
(n!)^2
(n!)2 要被
k
k
k 整除,那我们就是要找
(
n
!
)
2
(n!)^2
(n!)2 有多少个约数(因子) 就好啦。
此时,
x
=
(
n
!
)
2
k
+
n
!
x=\frac{(n!)^2}{k}+n!
x=k(n!)2+n!、
y
=
n
!
+
k
y=n!+k
y=n!+k
(
k
∈
N
∗
)
(k\in{N*})
(k∈N∗) 都是正整数,符合题意。
所以,我们把问题转化成:
(
n
!
)
2
共
有
多
少
个
正
因
子
(
约
数
)
(n!)^2共有多少个正因子(约数)
(n!)2共有多少个正因子(约数)
![](https://img-blog.csdnimg.cn/20200830090546202.jpg#pic_center)
即使问题分解到这里,乍一看还是挺吓人的,因为 ( n ! ) 2 (n!)^2 (n!)2可能会是一个大得吓人的数字, ( 1 0 6 ! ) 2 (10^6!)^2 (106!)2的数量级这谁顶得住。
讲到求一个数的因子个数,那就必须要想到唯一分解定理了,当然这篇博客有相关介绍。那我们就来复习一下下吧
- 每个大于1的自然数,要么本身就是质数,要么可以写为2个或以上的质数的积,而且这些质因子按大小排列之后,写法仅有一种方式。
例如:
6936= 2 3 2^3 23 x 3 x 17 1200= 2 4 2^4 24 x 3 x 5 2 5^2 52 - 用符号概括起来就是:对于任意的a,都有
a= p 1 e 1 p_1^{e_1} p1e1x p 2 e 2 p_2^{e_2} p2e2x…x p k e k p_k^{e_k} pkek, p i p_i pi为素数且 p 1 p_1 p1< p 2 p_2 p2<…< p k p_k pk。
那么a的总的因子数就是( e 1 e_1 e1+1) * ( e 2 e_2 e2+1) * ( e 3 e_3 e3+1)…( e k e_k ek+1)
有了唯一分解定理,我们就得想办法将 ( n ! ) 2 (n!)^2 (n!)2进行质数分解。毕竟 ( n ! ) 2 (n!)^2 (n!)2还是太大了,我们可以先对 n ! n! n! 进行质数分解,为什么呢?
- 我们假设 n ! = n!= n!= p 1 e 1 × p_1^{e_1}\times p1e1× p 2 e 2 × p_2^{e_2}\times p2e2×… × p k e k \times p_k^{e_k} ×pkek,那么 ( n ! ) 2 = (n!)^2= (n!)2= ( p 1 e 1 × (p_1^{e_1}\times (p1e1× p 2 e 2 × p_2^{e_2}\times p2e2×… × p k e k ) 2 \times p_k^{e_k})^2 ×pkek)2,再进一步运算得 ( n ! ) 2 = (n!)^2= (n!)2= p 1 2 e 1 × p_1^{2e_1}\times p12e1× p 2 2 e 2 × p_2^{2e_2}\times p22e2×… × p k 2 e k \times p_k^{2e_k} ×pk2ek。
- 若 n ! n! n! 的总的因子数是( e 1 e_1 e1+1) * ( e 2 e_2 e2+1) * ( e 3 e_3 e3+1)…( e k e_k ek+1),那么 ( n ! ) 2 (n!)^2 (n!)2 的总的因子数是( 2 e 1 2e_1 2e1+1) * ( 2 e 2 2e_2 2e2+1) * ( 2 e 3 2e_3 2e3+1)…( 2 e k 2e_k 2ek+1)
所以我们只要对 n ! n! n! 进行质数分解就好了,由于 n ! n! n!也挺大的。 n ! = 1 × 2 × 3 × . . . × ( n − 1 ) × n n! = 1 \times 2\times3\times... \times(n-1)\times n n!=1×2×3×...×(n−1)×n,我们只要通过for循环对1~n进行质数分解就好啦,并把结果累加到指数数组里面。
至于怎么得到筛选出质数进行分解,有挺多种方法的,线性筛也挺香的。这里就不赘述线性筛啦,有兴趣的小伙伴可以参考这篇博客。
![](https://img-blog.csdnimg.cn/20200830094725278.jpg#pic_center)
小伙伴们,最后我们来总结以下解法吧。题目的实质就是求 ( n ! ) 2 (n!)^2 (n!)2 共有多少个正因子(约数) ,由于 ( n ! ) 2 (n!)^2 (n!)2 的正因子数与 n ! n! n! 的正因子数是有联系的,我们可以先利用唯一分解定理先对 ( n ! ) (n!) (n!) 进行质数分解。求质数过程可以选择线性筛算法。对 ( n ! ) (n!) (n!) 进行质数分解的时候,需要注意用for循环依次对1~n进行质数分解,对指数数组进行累加。得到指数数组后便能够计算出答案啦!
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+100;
const int mod=1e9+7;
int n,tot=0;
ll ans=1;
int prime[N],pos[N],c[N]; //prime数组存放质数,pos[i]表示i的最小质数因子在prime中的位置,c[i]为指数
bool isnot_prime[N]; //isnot_prime[i]表示i是否为质数,是质数则为false
template<class T> void qr(T &x) //快读
{
int f=0;
x=0;
char c=getchar();
for(; !isdigit(c); c=getchar())
f^=c=='-';
for(; isdigit(c); c=getchar())
x=(x<<3)+(x<<1)+(c^48);
x=f?(-x):x;
}
template<class T> void qw(T x) //快写
{
if(x>=10)
qw(x/10);
putchar(x%10+'0');
}
void getPrime() //线性筛
{
for(int i=2; i<=n; i++)
{
if(!isnot_prime[i])
{
prime[++tot]=i;
pos[i]=tot;
}
for(int j=1; j<=tot&&i*prime[j]<=n; j++)
{
isnot_prime[i*prime[j]]=1;
pos[i*prime[j]]=j;
if(i%prime[j]==0)break;
}
}
}
void divide(int x) //将x分解成质因数的乘积
{
while(x!=1)
{
++c[pos[x]];
x/=prime[pos[x]];
}
}
int main()
{
#ifdef LOCAL
freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
#endif
qr(n);
getPrime();
for(int i=1; i<=n; i++) // 对n!进行素因数分解
divide(i);
for(int i=1; i<=N; i++)
if(c[i])
ans=(ans*(2LL*c[i]+1))%mod; //ans=(c[1]+1)*(c[2]+1)*...*(c[N]+1);
qw(ans);
puts("");
}
最后感谢小伙伴们的学习噢~
![](https://img-blog.csdnimg.cn/20200830095925823.png#pic_center)