卡特兰数(catalan)总结

卡特兰数的公式

递推公式1:$f(n)=sum limits_{i=0}^{n-1}f(i)*f(n-i-1)$

递推公式2:$f(n)=frac{f(n-1)*(4*n-2)}{n+1}$

组合公式1:$f(n)=frac{C_{2n}^{n}}{n+1}$

组合公式2:$f(n)=C_{2n}^{n}-C_{2n}^{n-1}$

关于卡特兰数的题目

1. 有限制的网格方案数   eg网格

利用组合数的思想:

对于长N宽M的网格(下图2),方案数为 $C_{n+m}^{m}-C_{n+m}^{m-1}$

理解:走到(n,m)这个点总共要走n+m步,其中有m步一定是向上的,所以$C_{n+m}^{m}$这是所有情况

   但有不合法的情况,且不合法的一定经过绿线,将原图形沿其翻折,相当于走到c点,此时总n+m步不变,但只有m-1步是向右的

   所以$C_{n+m}^{m-1}$是不合法的

                                                              (借用kaola学长的图)

对于N×N的网格就是卡特兰数了,如图一

 本题先将式子化简,然后将其分解质因数,消去除法,最后乘上每个质数的个数次方就好

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 using namespace std;
 6 int n,m,num,p[10005],v[10005];
 7 int sum[10005];
 8 void prime(int x)
 9 {
10     for(int i=2;i<=x;i++)
11     {
12         if(!v[i])    {v[i]=i;p[++num]=i;}
13         for(int j=1;j<=num;j++){
14             if(p[j]>v[i]||i*p[j]>x) continue;
15             v[i*p[j]]=p[j];
16         }
17     }
18 }
19 int len=1,ans[100000001];
20 void mul(int x)
21 {
22     int k=0;
23     for(int i=1;i<=len;i++)
24     {
25         ans[i]=ans[i]*x+k;
26         k=ans[i]/10;
27         ans[i]%=10;
28         if(k>0&&i==len)  len++;
29     }
30 }
31 int main()
32 {
33     ans[1]=1;
34     scanf("%d%d",&n,&m);
35     prime(n+m+1);
36     int t=n+1-m;
37     while(t>1)
38     {
39         sum[v[t]]++;
40         t/=v[t];
41     }
42     for(int i=n+m;i>=n+2;i--)
43     {
44         t=i;
45         while(t>1)
46         {
47             sum[v[t]]++;
48             t/=v[t];
49         }
50     }
51     for(int i=2;i<=m;i++)
52     {
53         t=i;
54         while(t>1)
55         {
56             sum[v[t]]--;
57             t/=v[t];
58         }
59     }
60     for(int i=1;i<=num;i++)
61         for(int j=1;j<=sum[p[i]];j++)
62             mul(p[i]);
63     for(int i=len;i>=1;i--)
64         printf("%d",ans[i]);
65     puts("");
66 }
View Code

 2.有趣的数列

其实这个也可以理解为上一个网格,将偶数位记为向右走一步,奇数位记为向上走一步,,偶数位之和大于奇数位之和,就是不能越过绿线

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #define ll  long long
 6 using namespace std;
 7 const int maxn=2000005;
 8 int n,mod,num;
 9 ll p[maxn];int v[maxn];
10 ll sum[maxn];
11 void prime(int x)
12 {
13     for(int i=2;i<=x;i++)
14     {
15         if(!v[i])    {v[i]=i;p[++num]=i;}
16         for(int j=1;j<=num;j++){
17             if(p[j]>v[i]||i*p[j]>x) break;
18             v[i*p[j]]=p[j];
19         }
20     }
21 }
22 ll qpow(int a,int b)
23 {
24     ll ans=1;
25     while(b)
26     {
27         if(b&1)  ans=ans*a%mod;
28         a=a*a%mod;
29         b>>=1;
30     }
31     return ans%mod;
32 }
33 int  main()
34 {
35     scanf("%d%d",&n,&mod);
36     prime(2*n+1);
37     for(int i=2*n;i>=n+2;i--)
38     {
39         int t=i;
40         while(t>1)
41         {
42             sum[v[t]]++;
43             t/=v[t];
44         }
45     }
46     for(int i=1;i<=n;i++)
47     {
48         int t=i;
49         while(t>1)
50         {
51             sum[v[t]]--;
52             t/=v[t];
53         }
54     }
55     ll ans=1;
56     for(int i=1;i<=num;i++)
57         if(sum[p[i]])
58             ans=ans*qpow(p[i],sum[p[i]])%mod;
59     printf("%lld
",ans);
60 }
View Code

3.树屋阶梯

 我们不妨手模样例,若扣去左下角直角所在矩形,

图一和图四的方案数为右面的2块的方案数×上面的0块的方案数,即为$f(3)+=f(2)*f(0)$

同理图二和图五为$f(3)+=f(0)*f(2)$   图三为$f(3)+=f(1)*f(1)$

由此可得  $f(n)=sum limits_{i=0}^{n-1}f(i)*f(n-i-1)$  卡特兰数公式1

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 using namespace std;
 6 int n,num,p[10005],v[10005];
 7 int sum[10005];
 8 void prime(int x)
 9 {
10     for(int i=2;i<=x;i++)
11     {
12         if(!v[i])    {v[i]=i;p[++num]=i;}
13         for(int j=1;j<=num;j++){
14             if(p[j]>v[i]||i*p[j]>x) break;
15             v[i*p[j]]=p[j];
16         }
17     }
18 }
19 int len=1,ans[10000001];
20 void mul(int x)
21 {
22     int k=0;
23     for(int i=1;i<=len;i++)
24     {
25         ans[i]=ans[i]*x+k;
26         k=ans[i]/10;
27         ans[i]%=10;
28         if(k>0&&i==len)  len++;
29     }
30 }
31 int main()
32 {
33     ans[1]=1;
34     scanf("%d",&n);
35     prime(2*n+1);
36     for(int i=2*n;i>=n+2;i--)
37     {
38         int t=i;
39         while(t>1)
40         {
41             sum[v[t]]++;
42             t/=v[t];
43         }
44     }
45     for(int i=1;i<=n;i++)
46     {
47         int t=i;
48         while(t>1)
49         {
50             sum[v[t]]--;
51             t/=v[t];
52         }
53     }
54     for(int i=1;i<=num;i++)
55         for(int j=1;j<=sum[p[i]];j++)
56             mul(p[i]);
57     for(int i=len;i>=1;i--)
58         printf("%d",ans[i]);
59     puts("");
60 }
View Code

关于卡特兰数的其他应用

1.出栈入栈问题:1,2,~n个数经过一个栈,合法的出栈序列$Cat(n)$

  (引用学长的课件)出栈次序是卡特兰数的一个应用。 我们将入栈视为+1,出栈视为-1,则限制条件为在任意位置前缀和不小于0 。 我们讨论这个问题与卡特兰数有什么关系。 为了方便,我们按入栈的先后顺序将各个元素由1到n编号。 假设最后一个出栈的数为k。 则在k入栈之前,比k小的数一定全部出栈,所以这部分方案数为h(k-1)。 在k入栈之后,比k大的数在k入栈之后入栈,在k出栈之前出栈,所以这部分的方案数为h(n-k)。 这两部分互不干扰,则方案数为h(k-1)*h(n-k) 枚举k,得到的公式就是卡特兰数的递推公式。

2.左括号与右括号的匹配问题:n个左括号和n个右括号组成的合法括号序列$Cat(n)$

  跟入栈出栈的理解是一样的

3.n个节点构成的二叉树的方案数为$Cat(n)$

  假设左子树有$i$个节点,右子树有$n-i-1$个节点,i从0到n-1,根据乘法原理

可得公式1$f(n)=sum limits_{i=0}^{n-1}f(i)*f(n-i-1)$

愿你在迷茫时,记起自己的珍贵。
原文地址:https://www.cnblogs.com/casun547/p/11222539.html