标签:des style blog http color os io for ar
3 100 4 10 0 0
8 2
题目大意:
解题思路:问你不超过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(组合数学-斯特林数,数论-整数快速幂,数论-求逆元)
标签:des style blog http color os io for ar
原文地址:http://blog.csdn.net/a1061747415/article/details/38820307