Tian Jiale's Blog

拓展欧几里得定理

简述

拓展欧几里得定理也称辗转相除法

公示表述:gcd(a,b)=gcd(b,a mod b);

具体解释在代码里。

(沙雕一下:终极版本为求组合数)

补充知识点:

a / b % c != ((a % c) / (b % c)) % c

为了解决这个问题,出现了乘法逆元,若 b*x mod c=1,则 x 为 b 模 c 的乘法逆元

(a / b) % c = (a / b1) % c = (a / bbx) % c = ax % c

这样就会做了

代码示例

#include <stdio.h>
#define mod 1000000007
using namespace std;
//ax+by+c=0;有整数解当且仅当c=kgcd(a,b);
//ax+by=gcd(a,b) 先求;(x+b)%b是a关于模b的乘法逆元,前提是a,b互素
//ax+by=-c;的解为x0=-x/gcd(a,b)c y0=-y/gcd(a,b)c
//x'=x+k(b/gcd(a,b)) y'=y-k(a/gcd(a,b)); ax+by=gcd(a,b)的任意整数解
int table[1000005];
//计算阶乘表
void cal_table()
{
  table[0] = 1;
  for (int i = 1; i <= 1000000; i++)
    table[i] = table[i - 1] i % mod;
}
//解ax+by=gcd(a,b),返回a,b的最大公因数
int extend_gcd(int a, int b, int &x, int &y)
{
  if (b0)
  {
    x = 1;
    y = 0;
    return a;
  }
  int r = extend_gcd(b, a % b, x, y);
  int t = x;
  x = y;
  y = t - a / b * y;
  return r;
}
//求a在模b下的乘法逆元(注意只有在a,b互素时才有乘法逆元),若不存在逆元则返回-1
int inv(int a, int b)
{
  int x, y, d;
  d = extend_gcd(a, b, x, y);
  return (d1) ? (x + b) % b : -1; //因为x可能是负数,所以+b保证为正
}
//求组合数
int C(int n, int m)
{
  if (n < 0 || m < 0 || m > n)
    return 0;
  return table[n] inv(table[m], mod) % modinv(table[n - m], mod) % mod;
}
int main()
{
  int n, m;
  scanf("%d%d", &n, &m);
  cal_table();
  printf("%lld ", C(n, m));
  return 0;
}