C++Legendre定理及其例题讲解—————方程

前言:

Legendre定理是一个你无法想象到的定理,而它的用处也是十分的大,现在亲听我慢慢讲解。

Legendre定理:

设n为一个正整数,那么在n!的标准素因子分解式中,素数p的最高次项为L_{p}(n!),则

                                                              L_{p}(n!)=\sum_{k\geq1} \left [ \frac{n}{p^k} \right ]

当模数不为素数,且不方便使用CRT进行合并时,可以考虑对组合数分解质因数,由于组合数可以写为一个阶乘除以两个阶乘的形式,可以对这三个阶乘分别分解质因数,然后使用指数的减法,得到最后组合数的标准分解。

证明我就不在这里细说了,反正这个定理可以让我们快速求出p的指数。

下面是例题。

题目描述:

求方程x_{1}+x_2+......x_k=n的非负整数解的组数

n,k≤100,000,答案对20080814取模

输入:

仅一行,包含两个正整数n,k。

输出:

一个整数,表示方程不同解的个数,这个数可能很大,你只需输出mod 20080814 的结果。

样例输入:

1 1

样例输出:

1

思路分析:

利用隔板法,答案为\LARGE C_{n+1}^{k-1}

由于20080814=2*13*772339(3个均为质因数),故可以用CRT

也可以用Legendre定理分解阶乘的质因数。

但是此题的输入有坑,n,k需要特殊处理,详见代码。

代码实现:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define ll long long
ll n,m,p[200005],w[200005],k,ans=1;
bool v[200005];
ll olsf()//欧拉筛法求素数
{
    for(ll i=2;i<=n+1;i++)
    {
        if(!v[i])
        {
            k++;
            p[k]=i;
            v[i] = 1;
        }
        for(ll j=1;j<=k&&p[j]*i<=n+1;j++)
        {
            v[i*p[j]]=1;
            if(i%p[j]==0)
                break;
        }
    }
}
ll qkpow(ll x,ll y)//快速幂不解释
{
    ll ans=1;
    while(y)
    {
        if(y&1)
            ans=(ans*x)%20080814;
        x=(x*x)%20080814;
        y/=2;
    }
    return ans%20080814;
}
int main()
{
    scanf("%lld%lld",&m,&n);
    n=n+m-1;
    m--;
    olsf();
    for(ll i=1;i<=k;i++)//求分子的分解
        for(ll j=p[i];j<=n;j*=p[i])
            w[i]+=n/j;
    for(ll i=1;i<=k;i++)//求分母的分解
        for(ll j=p[i];j<=m;j*=p[i])
            w[i]-=m/j;
    for(ll i=1;i<=k;i++)
        for(ll j=p[i];j<=n-m;j*=p[i])
            w[i]-=(n-m)/j;
    for(ll i=1;i<=k;i++)
        ans=(ans*qkpow(p[i],w[i]))%20080814;
    printf("%lld",ans);
}

鸣谢大佬的详解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章