卡特兰数,扩欧求逆元
题目意思:
n个节点的二叉树,可以构成有 catalan[n] 种不同的二叉树。 sum[n] 是卡特兰数前 n项的总和。
每次给出两个数 n, m, 求 sum[n] % m 是多少。
本题要点:
1、n很大,如果用大数来存第 100000 个卡特兰数,肯定超时。
2、卡特兰数的 递推公式
h[i] = h[i - 1] * (4 * i - 2) / (i + 1)
要求每一个卡特兰数 对m的模, 这里有除法,需要 求 (i + 1) 在模 m的情况下的逆元。
3、扩展欧几里得算法,求逆元
同余式 ax = 1(mod m), 如果 a, m 互质, 那么必然存在x, 使得 ax = 1(mod m)成立。
x 就是 a 在模 m下的逆元。 式子 ax = 1(mod m), 变形 ax - m*y = 1, 右扩展欧几里得算法,
直接求出x。
4、 本题中, (4 * i - 2) 与 m 不一定互质, (i + 1) 与 m 不一定互质,通过 m 进行因数分解来解决:
m 进行因数分解:
vector vc; // 存放 m 的各个素因子
5、 观察 式子 h[i] = h[i - 1] * (4 * i - 2) / (i + 1), 第一个卡特兰数 h[1] = 1
然后全部展开的话, h[i] = 1 * (6 / 3) * (10 / 4) * (14 / 5) * … * ( (4 * i - 2) / (i + 1) )
分子部分 (4 * i - 2) 所有的乘积,必然能够整除 所有 分母部分 (i + 1) 的所有乘积。
然后对于分子 temp = (4 * i - 2) ,如果能整除m的某个素因子 vc[j], temp /= vc[j], 同时 cnt[j]++;
假设除剩下来的 是 fz , 那么 fz 与 m 互质。
然后对于分母 temp = (i + 1) ,如果能整除m的某个素因子 vc[j], temp /= vc[j], 同时 cnt[j]–;
假设除剩下来的 是 fm , 那么 fm 与 m 互质。 可以用扩展欧几里得算法, 求 fm 在模m下的逆元了。
对于每一个 cnt[j], 不会有负数的。具体实现看代码,比较明了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
const int MaxN = 100010;
typedef long long LL;
LL n, m;
int cnt[MaxN]; //存放相应的次数
vector<LL> vc; // 存放 m 的各个素因子
LL exgcd(LL a, LL b, LL&x, LL& y)
{
if(!b)
{
x = 1