浅谈求卡特兰数的几种方法

Posted 天下风云出我辈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈求卡特兰数的几种方法相关的知识,希望对你有一定的参考价值。

  卡特兰数是一个很常见的数列,以比利时的数学家欧仁·查理·卡塔兰 (1814–1894)的名字来命名,其前几项为 : 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, ...(摘自百度百科)

 

卡特兰数的求解方法

1.最基本的n^2递推

 例题:https://www.luogu.org/problemnew/show/P1044

技术分享图片
 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 int n;
 5 int f[20];
 6 int main()
 7 {
 8     scanf("%d",&n);
 9     f[0]=1;
10     f[1]=1,f[2]=2;
11     for(int i=3;i<=n;++i)
12         for(int j=0;j<i;++j)
13         f[i]+=f[j]*f[i-j-1];
14     cout<<f[n]<<endl;
15     return 0;
16 } 
View Code

此题由于数据范围非常小,既不会t也不会炸int,所以直接n^2求即可。证明不会qwq。

2.卡特兰数的第n项h(n)=C(2n,n)-C(2n,n-1),所以用求组合数的方法求卡特兰数即可,针对对一个大质数取模的代码

技术分享图片
 1 #include<iostream>
 2 #include<cstdio> 
 3 using namespace std;
 4 int n,p;
 5 int js[100005];
 6 int prime[100005];
 7 bool vis[100005];
 8 int cnt[100005];
 9 int qpow(int a,int b)//快速幂求逆元 
10 {
11     int ans=1;
12     while(b)
13     {
14         if(b&1)ans=(1ll*a*ans)%p;
15         a=(1ll*a*a)%p;
16         b>>=1;
17     }
18     return ans;
19 }
20 int main()
21 {
22     scanf("%d%d",&n,&p);//求第n项,对p取模的结果,p为大质数
23     js[0]=1;
24     for(int i=1;i<=2*n;++i)
25         js[i]=(1ll*js[i-1]*i)%p;
26     int a=qpow(1ll*js[n]*js[n]%p,p-2),b=qpow(1ll*js[n-1]*js[n+1]%p,p-2);
27     a=1ll*a*js[2*n]%p,b=1ll*b*js[2*n]%p;
28     int ans=(a-b+p)%p;//a=C(2n,n)%p,b=C(2n,n-1)%p 
29     printf("%d",ans);
30     return 0;
31 }
View Code

3.我们可以由第一种求法看出来卡特兰数增长的是很快的,所以当要求的项数比较大而且不能取模时,需要用到高精,这时分解质因数求卡特兰数就是一个很好的方法。

  例题:https://www.luogu.org/problemnew/show/P2532

这题蒟蒻做的时候看到样例输入3,输出5,直接想到卡特兰数,没想到是对的,233333333

代码

技术分享图片
 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 int n; 
 5 int prime[1500],cnt;
 6 bool vis[1550];
 7 int tong[1050];
 8 int ans[1100],len;
 9 void cheng(int x,int sum)//高精乘低精
10 {
11     while(sum--)
12     {
13         for(int i=1;i<=len;++i)ans[i]*=x;
14         for(int i=1;i<=len;++i)
15         {
16             if(ans[i]>9)
17             {
18                 ans[i+1]+=ans[i]/10,ans[i]%=10;
19                 if(i+1>len)len++;
20             }
21         }
22     }
23 }
24 int main()
25 {
26     scanf("%d",&n);
27     vis[0]=vis[1]=1;
28     for(int i=2;i<=2*n;++i)//分解质因数
29     {
30         if(!vis[i])prime[++cnt]=i;
31         for(int j=1;j<=cnt&&prime[j]*i<=2*n;++j)
32         {
33             vis[i*prime[j]]=1;
34             if(i%prime[j]==0)break;
35         }
36     }
37     for(int i=1;i<=cnt;++i)
38     {
39         int tmp=2*n;
40         while(tmp/prime[i]>0)tong[i]+=tmp/prime[i],tmp/=prime[i];
41         tmp=n;while(tmp/prime[i]>0)tong[i]-=tmp/prime[i],tmp/=prime[i];
42         tmp=n+1;while(tmp/prime[i]>0)tong[i]-=tmp/prime[i],tmp/=prime[i]; 
43     }
44     ans[1]=1,len=1;
45     for(int i=1;i<=cnt;++i)
46     {
47         if(tong[i])
48         {
49             cheng(prime[i],tong[i]);
50         }
51     }
52     for(int i=len;i>=1;i--)printf("%d",ans[i]);
53     return 0;
54 }
View Code

4.卡特兰数的递推公式h(n)=h(n-1)*(4*n-2)/(n+1),不过蒟蒻因为不是很懂,总是觉得没谱,所以从来没有用过这个递推式。

 

以上是关于浅谈求卡特兰数的几种方法的主要内容,如果未能解决你的问题,请参考以下文章

iOS之浅谈纯代码控制UIViewController视图控制器跳转界面的几种方法

iOS之浅谈纯代码控制UIViewController视图控制器跳转界面的几种方法

浅谈数据抓取的几种方法

Laravel:如何在控制器的几种方法中重用代码片段

浅谈http的几种请求方法

浅谈java开启异步线程的几种方法(@Async,AsyncManager,线程池)