题目链接:
https://www.luogu.com.cn/problem/P4720
与卢卡斯定理的区别:
在求解 的问题上,lucas方法中p必须为质数,Exlucas方法中p不一定是质数,也可以是合数。
Exlucas作用:
求解 p为非质数的 问题
方法中会使用快速幂,中国剩余定理等。
ac代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5+5;
int a[N],b[N];
int qmi(int a,int b,int p)
{
int ans = 1;
while(b)
{
if(b&1)
ans = ans*a%p;
b >>= 1;
a = a*a%p;
}
return ans;
}
int exgcd(int a,int b,int &x,int &y)
{
if(!b)
{
x=1,y=0;
return a;
}
int d = exgcd(b,a%b,y,x);
y -= a/b*x;
return d;
}
int inv(int a,int p)
{
int x,y;
int d = exgcd(a,p,x,y);
return (x%p+p)%p;
}
int china(int n,int p)
{
int ans = 0;
for (int i=1;i<=n;i++)
{
int mi = p/a[i];
int x = inv(mi,a[i]);
ans = (ans+b[i]*mi%p*x%p)%p;
}
return ans;
}
int g(int n,int p)
{
if(n<p)
return 0;
return g(n/p,p)+n/p;
}
int f(int n,int p,int pk)
{
if(!n)return 1;
int rou = 1;
int rem = 1;
for (int i=1;i<=pk;i++)
if(i%p)
rou = rou*i%pk;
rou = qmi(rou,n/pk,pk);
for (int i=pk*(n/pk);i<=n;i++)
if(i%p)
rem = rem*(i%pk)%pk;
return f(n/p,p,pk)*rou%pk*rem%pk;
}
int c_pk(int n,int m,int p,int pk)
{
int fn = f(n,p,pk),fm = inv(f(m,p,pk),pk),fnm = inv(f(n-m,p,pk),pk);
int mi = qmi(p,g(n,p)-g(m,p)-g(n-m,p),pk);
return fn*fm%pk*fnm%pk*mi%pk;
}
int exlucas(int n,int m,int p)
{
int tmp = p,cnt = 0;
for (int i=2;i<=tmp/i;i++)
if(tmp%i==0)
{
int pk = 1;
while(tmp%i==0)
tmp/=i,pk*=i;
a[++cnt] = pk;
b[cnt] = c_pk(n,m,i,pk);
}
if(tmp>1)
{
a[++cnt] = tmp;
b[cnt] = c_pk(n,m,tmp,tmp);
}
return china(cnt,p);
}
signed main ()
{
int n,m,p;
cin>>n>>m>>p;
cout<<exlucas(n,m,p);
return 0;
}