神奇的组合数学——卡特兰数

卡特兰数:

定义: h(0)=1,h(1)=1 ,Catalan数满足递推式。 h(n)=h(0)×h(n1)+h(1)×h(n2)+...+h(n1)×h(0)(n2)
通项公式
这里写图片描述

证明如下(摘自TAOCP的原练习题)(数竞党很少用不严格证明的定理):
神奇的组合数学——卡特兰数_第1张图片
神奇的组合数学——卡特兰数_第2张图片

神奇的组合数学——卡特兰数_第3张图片

解决问题:

1.括号对:合法的括号对对数答案为 hn
2.二叉树:合法的树有答案为 hn
3.分多边形:有 hn 种分法,用这个可以简单的证明卡特兰数的通项:
不妨设 hi i 边形的答案,明显,有 hi=h(n)=h(1)×h(n1)+h(2)×h(n2)+...+h(n)h(0)
又因为我们可以得到,总共的分法有 C(n,n2) 个,不合法的是一个“逆序相交”,从而不合法的有: C(nt,t)C(t,1)
故相减可得有 所以得证(肯定有人注意到了,这个 hn 的式子与卡特兰数的不符合,实际上这是卡特兰数的另一种递推式)。
例题:
?题?(别笑,题目就叫“题”)
题目:

出个题就好了.这就是出题人没有写题目背景的原因.
你在平面直角坐标系上.
你一开始位于(0,0).
每次可以在上/下/左/右四个方向中选一个走一步.
即:从(x,y)走到(x,y+1),(x,y-1),(x-1,y),(x+1,y)四个位置中的其中一个.
允许你走的步数已经确定为n.现在你想走n步之后回到(0,0).但这太简单了.你希望知道有多少种不同的方案能够使你在n步之后回到(0,0).当且仅当两种方案至少有一步走的方向不同,这两种方案被认为是不同的.
答案可能很大所以只需要输出答案对10^9+7取模后的结果.(10^9+7=1000000007,17之间有80)
这还是太简单了,所以你给能够到达的格点加上了一些限制.一共有三种限制,加上没有限制的情况,一共有四种情况,用0,1,2,3标号:
0.没有任何限制,可以到达坐标系上所有的点,即能到达的点集为{(x,y)|x,y为整数}
1.只允许到达x轴非负半轴上的点.即能到达的点集为{(x,y)|x为非负数,y=0}
2.只允许到达坐标轴上的点.即能到达的点集为{(x,y)|x=0或y=0}
3.只允许到达x轴非负半轴上的点,y轴非负半轴上的点以及第1象限的点.即能到达的点集为{(x,y)|x>=0,y>=0}
【输入格式】
一行两个整数(空格隔开)n和typ,分别表示你必须恰好走的步数和限制的种类.typ的含义见【题目描述】.
【输出格式】
一行一个整数ans,表示不同的方案数对10^9+7取模后的结果.
【样例输入0100 0
【样例输出0383726909
【样例输入1100 1
【样例输出1265470434
【样例输入2100 2
【样例输出2376611634
【样例输入3100 3
【样例输出3627595255
【数据范围】
10%的数据,typ=0,n<=100
10%的数据,typ=0,n<=1000
5%的数据, typ=0,n<=100000
10%的数据,typ=1,n<=100
10%的数据,typ=1,n<=1000
5%的数据, typ=1,n<=100000
10%的数据,typ=2,n<=100
15%的数据,typ=2,n<=1000
10%的数据,typ=3,n<=100
10%的数据,typ=3,n<=1000
5%的数据, typ=3,n<=100000
以上11部分数据没有交集.
100%的数据,保证n为偶数,2<=n<=100000,0<=typ<=3.

题解:
明显第二个的答案就是 hn ,第四个可以看成是两个维度的第二题,所以形式应该是 hn×hfn ,然后待定系数法确定系数,再用数学归纳法证明一下就可以了,结果是 CatnCatn+2 Catx 就是指卡特兰数的第x项)。第一个易推为 [C(n,n2)]2 ,又注意到第三个范围极小,从而dp解决(好吧,好像就没有公式,所以只有1000) ,就是加法原理做啊。代码如下。
代码:

#include
#include
typedef long long ll;
const ll mod = 1000000007;
using namespace std;
ll q_pow(ll a,ll b)
{
    ll ans = 1;
    while (b)
    {
        if (b%2==1) (ans*=a)%=mod;
        b/=2; 
        (a*=a)%=mod;
    }
    return ans%mod;
}

ll catalan(ll n)
{
    ll wt = 1 , xiaowt=1;
    for (ll i = 1;i<=2*n;i++)
    (wt*=i)%=mod;
    for (ll i =1; i<=n;i++)
    (xiaowt*=i)%=mod;

    ll wtt = q_pow(xiaowt,mod-2);
        (wtt*=wtt)%=mod;
    (wtt *= q_pow(n+1,mod-2) )%=mod;

    return (wt * wtt + mod)%mod;
}

ll n , typ;
int main()
{
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    scanf("%lld%lld",&n,&typ);
    if (typ == 0)
    {
        ll wt = 1;
        ll xiaowt = 1;

        for (ll i =1;i<=n;i++)
        (wt*=i)%=mod;

        for (ll i =1;i<=n/2;i++)
        (xiaowt*=i)%=mod;

        ll wtt = q_pow(xiaowt,mod-2);
        (wtt*=wtt)%=mod;

        ll answer = (wt*wtt +mod) %mod;
        (answer*=answer)%=mod;
        printf("%lld",answer);
    }
    else 
    if (typ==1)
    {
        ll asw = catalan(n/2);
        (asw+=mod)%=mod;
        printf("%lld",asw);
    }
    else

    if (typ==2)//水水的dp,加法原理
    {
        ll dp[2][2*n+10][2*n+10];   //1 y 0 x
        dp [0][1][n+2] = 1; dp [1][1][n+2] = 1;//1
        dp [0][0][n+1] = 1; dp[1][0][n+1] = 1;//0
        dp [0][1][n] = 1; dp [1][1][n] = 1;//-1
        for (ll i = 1; i <= n;i++)
            {
                for (int j = 1 ;j <= 2 * n + 1 ; j++)
                    {
                    dp[0][i][j] = (dp[0][i-1][j+1] + dp [0][i-1][j-1])%mod; 
                    dp[1][i][j] = (dp[1][i-1][j+1] + dp [1][i-1][j-1])%mod; 
                    }
                dp[0][i][n+1] =  (dp[0][i-1][n + 2] + dp[1][i-1][n + 2] + dp[0][i-1][n] + dp[1][i-1][n] )%mod;
                dp[1][i][n+1] =  dp[0][i][n+1];
            }   
        printf("%lld\n",dp[0][n][n+1]);
    }
    else
    if (typ==3)
    {
    ll answ = ( catalan(n/2) * catalan( (n+2)/2 ))%mod;
    printf("%lld",answ);
    }
    return 0;
}

结语

这些数学题好啊,以后要多学点。

你可能感兴趣的:(组合数学)