HDU-3240(卡特兰数+分解质因数后求逆元)

卡特兰数相关公式 :

  1. \(H_n = {C_{2n}^n \over n+1)}\)
  2. \(H_n = {(4n-2)\over n+1}\times H_{n-1}\)
  3. \(H_n = C_{2n}^n - C_{2n}^{n-1}\)
  4. $ H_n = \begin{cases} \sum_{i=1}^{n} H_{i-1} H_{n-i} & n \geq 2, n \in \mathbf{N_{+}}\ 1 & n = 0, 1 \end{cases} $

因为 \(n\le 100000\) ,所以不考虑第四种,第一种和第三种种求组合数也不可以用递推来求,而用公式的话因为要预先算出阶乘和阶乘的逆元,在此题中m不保证为质数,所以也不好计算。

对于第三种,假设以及算出了\(H_{n-1}\) 那么只需要求出\({4n-2\over n+1}\) 即可

先把 m 分解质因数,然后对于分子 \(4n-2\) ,求出m分解后的质因子的所有指数,然后对于分母\(n+1\) ,也求出m分解的质因子的所有指数,前后两个指数数组相减。剩下分子和分母与m互质的部分直接求即可,保留到递推答案pre中。

综上,当前递推的答案由一个变量pre 和一个指数数组共同记录,所以在地推出当前答案之后进行累加时,还要用pre乘所有质因子

#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
typedef long long ll;
int n,m;
int cnt[N];
vector<ll> v;
void exgcd(ll a,ll b,ll& d,ll& x,ll& y) {
    if(!b) {
        d = a; x = 1; y = 0;
    }else{
        exgcd(b,a%b,d,y,x); y -= x*(a/b);
    }
}
ll inv(ll a,ll n){
    ll d,x,y;
    exgcd(a,n,d,x,y);
    return d== 1?(x+n)%n:-1;
}
void getPrime(int x){
    for(int i=2;i*i<=x;i++){
        if(x % i)continue;
        while(x%i==0)x/=i;
        v.push_back(i);
    }
    if(x > 1)v.push_back(x);
    return;
}
int main(){
    while(cin >> n >> m){
        if(n == 0 && m == 0)break;
        v.clear();
        memset(cnt,0,sizeof cnt);
        ll res = 1 % m;
        ll pre = 1;
        getPrime(m);//对m进行分解质因数
        for(int i=2;i<=n;i++){
            ll fz = 4 * i - 2, fm = i + 1;
            for(int j=0;j<v.size();j++){
                if(fz % v[j] == 0)
                while(fz % v[j] == 0){
                    fz /= v[j];
                    cnt[j] ++;//指数++
                }
            }
            pre = pre * fz % m;//剩余互质部分直接乘
            for(int j=0;j<v.size();j++){
                if(fm%v[j] == 0)
                while(fm % v[j] == 0){
                    fm /= v[j];
                    cnt[j] --;//指数--
                }
            }
            if(fm > 1) pre = pre * inv(fm,m) % m;//更新pre
            ll tmp = pre;
            for(int j=0;j<v.size();j++){
                for(int k=1;k<=cnt[j];k++){
                    tmp = (tmp * v[j]) % m;//计算当前答案
                }
            }
            res = (res + tmp) % m;
        }
        cout << res << endl;
    }
    return 0;
}

为什么不是每次直接把质因子直接计算到pre中呢,因为之后的计算中,对分母(n+1)进行分解质因数时,有可能出现不够减的情况,所以我们要一直用一个数组记录这部分和m有公因数的部分

posted @ 2019-09-04 23:55  kpole  阅读(277)  评论(0编辑  收藏  举报