题目
分析和思路
上图来自卢卡斯定理 - OI Wiki (oi-wiki.org)
实现:前一项递归调用,而后面一项算出组合数
// C++ Version
long long Lucas(long long n, long long m, long long p) {
if (m == 0) return 1;
return (Lucas(n / p, m / p, p) * C(n % p, m % p, p)) % p;
}
# Python Version
def Lucas(n, m, p):
if m == 0:
return 1
return (Lucas(n // p, m // p, p) * C(n % p, m % p, p)) % p
计算组合数的几种方法总结_liuzibujian的博客_(C++)
Python快速求组合数C(n,m)三种方法整理_果冻er的博客(python)
这道题的数学原理我还是没搞明白,思路是看上面大佬的博客,代码是套用网上大佬的模板写的,先放这以后如果弄懂了再回头补上原理解释
代码实现
C++
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, m, p;
//扩展欧几里得算法 求最大公约数,如果返回1表示a,b互质
ll exgcd(ll a, ll b, ll &x, ll &y) //返回ab最大公因数,x为a%b逆元
{
if (b == 0)
{
x = 1, y = 0;
return a; //到达递归边界,返回上一层
}
ll ret = exgcd(b, a%b, x, y);//递归
ll temp = y;//把x y回归上一层
y = x - (a/b)*y;
x = temp;
return ret;
}
//求a%b的逆元
ll inv(ll a, ll b)
{
ll x,y;
ll g = exgcd(a, b, x, y);
if (g == 1) return (x%b + b) % b;
return -1;//互质就返回-1
}
ll Cm(ll n, ll m, ll p)
{
ll a = 1, b = 1;
if (m > n) return 0;
while (m)
{
a = (a*n) % p;
b = (b*m) % p;
m--;
n--;
}
return (ll)a*inv(b, p) % p;
}
int Lucas(ll n, ll m, ll p)
{
if (m == 0) return 1;
return (ll)Cm(n%p, m%p, p)*(ll)Lucas(n / p, m / p, p) % p;
}
int main()
{
int T;
cin >> T;
while (T--)
{
cin>>n>>m>>p;
printf("%d\n", Lucas(n + m, n, p));
}
return 0;
}
拓展
分享一篇很不错的文章:
⭐感谢您能看到这里!⭐