首页 > 代码库 > HDU 3240 Counting Binary Trees(组合数学-斯特林数,数论-整数快速幂,数论-求逆元)

HDU 3240 Counting Binary Trees(组合数学-斯特林数,数论-整数快速幂,数论-求逆元)

Counting Binary Trees



Problem Description
There are 5 distinct binary trees of 3 nodes:

Let T(n) be the number of distinct non-empty binary trees of no more than n nodes, your task is to calculate T(n) mod m.
 

Input
The input contains at most 10 test cases. Each case contains two integers n and m (1 <= n <= 100,000, 1 <= m <= 109) on a single line. The input ends with n = m = 0.
 

Output
For each test case, print T(n) mod m.
 

Sample Input
3 100 4 10 0 0
 

Sample Output
8 2
 

Source
2009 “NIT Cup” National Invitational Contest
 

Recommend
zhonglihua   |   We have carefully selected several similar problems for you:  3249 3241 3242 3243 3244 
 

题目大意:

问你不超过n个节点的二叉树的方案数,结果要对m求余。


解题思路:

方案数可以划分左右两边划分子问题也就是 h(n)=h(0)*h(n-1)+h(1)*(h-2)+..................+h(n-1)*h(0),一看就是卡特兰数,

关于卡特兰数必须要nlg^n的左右效率的算法解决

百度一下,知道:

令h(0)=1,h(1)=1,

catalan数满足:

递推式[1] :h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)h(0) (n>=2)
递推式[2]  :h(n)=h(n-1)*(4*n-2)/(n+1);
递推式[3] :h(n)=C(2n,n)/(n+1) (n=0,1,2,...)
递推式[4] :h(n)=c(2n,n)-c(2n,n+1)(n=0,1,2,...)

我们选择,递推式[2]  :h(n)=h(n-1)*(4*n-2)/(n+1);

但是这个递推要求余,乘法求余没关系,除法要求逆元,除数有逆元的充要条件是 gcd(a,mod)==1,也就是除数与要模的那个数互质。

那么,我们对于mod分解因子,对于分母中的因子

(1)互质的数,分母直接求逆元

(2)不互质的数,无法求逆元,只能用分子去消。


解题代码:

#include <iostream>
#include <cstdio>
#include <map>
#include <vector>
#include <cstring>
using namespace std;

typedef long long ll;
const int maxn=50;
int tol,prime[maxn],cnt[maxn],n,mod;

void getPrime(){
    tol=0;
    int x=mod;
    for(int i=2;i*i<=x;i++){
        if(x%i==0){
            prime[tol++]=i;
            while(x%i==0) x/=i;
        }
    }
    if(x>1) prime[tol++]=x;
}

void extend_gcd(int a,int b,int &x,int &y){
     if(b==0){
        x=1;
        y=0;
     }else{
        extend_gcd(b,a%b,x,y);
        int tmp=x;
        x=y;
        y=tmp-(a/b)*y;
     }
}

int inv(int a,int mod){
    int x,y;
    extend_gcd(a,mod,x,y);
    return (x%mod+mod)%mod;
}

void deal1(ll &ret,int x){
    for(int i=0;i<tol;i++){
        while(x%prime[i]==0){
            x/=prime[i];
            cnt[i]++;
        }
    }
    ret=(ret*x)%mod;
}

void deal2(ll &ret,int x){
    for(int i=0;i<tol;i++){
        while(x%prime[i]==0){
            x/=prime[i];
            cnt[i]--;
        }
    }
    if(x>1){
        int tmp=inv(x,mod);
        ret=(ret*tmp)%mod;
    }
}

ll pow_mod(ll a,ll b,ll p){
    ll sum=1;
    while(b>0){
        if(b&1) sum=(sum*a)%p;
        a=(a*a)%p;
        b/=2;
    }
    return sum%p;
}

void solve(){
    ll ans=1,ret=1;
    memset(cnt,0,sizeof(cnt));
    getPrime();
    for(int i=2;i<=n;i++){
        deal1(ret,4*i-2);
        deal2(ret,i+1);
        ll tmp=ret;
        for(int t=0;t<tol;t++){
            tmp=( tmp*pow_mod(prime[t],cnt[t],mod) )%mod;
        }
        ans=(ans+tmp)%mod;
    }
    cout<<ans<<endl;
}

int main(){
    while(cin>>n>>mod && (n||mod) ){
        solve();
    }
    return 0;
}







HDU 3240 Counting Binary Trees(组合数学-斯特林数,数论-整数快速幂,数论-求逆元)