樱花(线性筛+唯一分解定理)

樱花(线性筛+唯一分解定理)

题目链接: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

这题很显然是数论题,下面我们来进行数学推导:


因为 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(1n106)
所以 1 x 、 1 y < 1 n ! , 即 x 、 y > n ! \frac{1}{x}、\frac{1}{y}<\frac{1}{n!} ,即x、y>n! x1y1<n!1xy>n!
由于 x 、 y > n ! x、y>n! xy>n!,我们不妨令
y = n ! + k ( k ∈ N ∗ ) y=n!+k(k\in{N*}) y=n!+k(kN)
我们将 y = n ! + k ( k ∈ N ∗ ) y=n!+k(k\in{N*}) y=n!+k(kN)代入 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!这条关键公式,因为这个公式太重要了,所以重要的事情说三遍,嘿嘿。


因为 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*}) (kN) 都是正整数,符合题意。


所以,我们把问题转化成:
( n ! ) 2 共 有 多 少 个 正 因 子 ( 约 数 ) (n!)^2共有多少个正因子(约数) (n!)2


即使问题分解到这里,乍一看还是挺吓人的,因为 ( 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×...×(n1)×n,我们只要通过for循环对1~n进行质数分解就好啦,并把结果累加到指数数组里面。

至于怎么得到筛选出质数进行分解,有挺多种方法的,线性筛也挺香的。这里就不赘述线性筛啦,有兴趣的小伙伴可以参考这篇博客



小伙伴们,最后我们来总结以下解法吧。题目的实质就是求 ( 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("");

}



最后感谢小伙伴们的学习噢~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

__Wedream__

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

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

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

打赏作者

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

抵扣说明:

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

余额充值