[HDU3240]Counting Binary Trees(不互质同余除法)

Counting Binary Trees

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 564    Accepted Submission(s): 184

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

题意:求Catalan数的前n项和。

直接递推公式就好了,但是有一个问题,递推式里有除法,而由于除数与模数不互质,不能预处理逆元,这里有一个求不互质同余除法的方法(前提是结果必须是整数,所以只能用来求Catalan,Stirling和组合数这样的数)

$frac{a}{b}equiv c (mod   d)$,我们先将d质因数分解,然后对于$a$和$b$将d的质因子部分单独统计,剩余部分直接exgcd求逆元即可。

因为剩余部分满足互质所以可以直接做逆元,而我们有$pequiv p(mod ap)$,所以最后质因子部分直接乘就可以了。

这样就解决了HNOI2017的70分做法。

#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define rep(i,l,r) for (int i=l; i<=r; i++)
using namespace std;

const int maxn=100010;
typedef long long ll;

ll ans,cnt[maxn];
vector<int> prime;
int n,m;

void exgcd(ll a,ll b,ll &x,ll &y){
    if (!b) x=1,y=0;
    else exgcd(b,a%b,y,x),y-=x*(a/b);
}

ll inv(ll a,ll p){ ll x,y; exgcd(a,p,x,y); return (x+p)%p; }

void getPrime(){
    ll t=m;
    for (int i=2; i*i<=t; i++)
        if (t%i==0){
            prime.push_back(i);
            while (t%i==0) t/=i;
        }
    if (t>1) prime.push_back(t);
}

void solve(){
    getPrime(); ans=1; ll res=1;
    rep(i,2,n){
        ll fz=4*i-2,fm=i+1;
        for (int k=0; k<(int)prime.size(); k++)
            if (fz%prime[k]==0)
                while (fz%prime[k]==0) fz/=prime[k],cnt[k]++;
        res=(res*fz)%m;
        for (int k=0; k<(int)prime.size(); k++){
            if (fm%prime[k]==0)
                while (fm%prime[k]==0) fm/=prime[k],cnt[k]--;
        }
        if (fm>1) res=(res*inv(fm,m))%m;
        ll t=res;
        for (int k=0; k<(int)prime.size(); k++)
            rep(s,1,cnt[k]) t=(t*prime[k])%m;
        ans=(ans+t)%m;
    }
    printf("%lld
",ans);
}

int main(){
    while(~scanf("%d%d",&n,&m) && n+m)
        prime.clear(),memset(cnt,0,sizeof(cnt)),solve();
    return 0;
}
原文地址:https://www.cnblogs.com/HocRiser/p/8628987.html