前言:
Legendre定理是一个你无法想象到的定理,而它的用处也是十分的大,现在亲听我慢慢讲解。
Legendre定理:
设n为一个正整数,那么在的标准素因子分解式中,素数p的最高次项为,则
当模数不为素数,且不方便使用CRT进行合并时,可以考虑对组合数分解质因数,由于组合数可以写为一个阶乘除以两个阶乘的形式,可以对这三个阶乘分别分解质因数,然后使用指数的减法,得到最后组合数的标准分解。
证明我就不在这里细说了,反正这个定理可以让我们快速求出p的指数。
下面是例题。
题目描述:
求方程的非负整数解的组数
n,k≤100,000,答案对20080814取模
输入:
仅一行,包含两个正整数n,k。
输出:
一个整数,表示方程不同解的个数,这个数可能很大,你只需输出mod 20080814 的结果。
样例输入:
1 1
样例输出:
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);
}
鸣谢大佬的详解。