洛谷P1976 鸡蛋饼

洛谷P1976 鸡蛋饼

题目背景

Czyzoiers \text{Czyzoiers} Czyzoiers 想知道小 x x x 为什么对鸡蛋饼情有独钟。经过一番逼问,小 x x x 道出了实情:因为他喜欢圆。

题目描述

最近小 x x x 又发现了一个关于圆的有趣的问题:在圆上有 2 N 2N 2N 个不同的点,小 x x x 想用 N N N 条线段把这些点连接起来(每个点只能连一条线段),使所有的线段都不相交,他想知道这样的连接方案有多少种。

输入输出格式

输入格式:
有且仅有一个正整数 N N N 。( N ⩽ 2999 N \leqslant 2999 N2999
输出格式:
要求的方案数(结果 mod 100000007 \text{mod}100000007 mod100000007)。

输入输出样例

输入样例#1
24
输出样例#1
4057031


题目分析

特例分析

我们先来考虑 4 4 4 个点的情形
在这里插入图片描述
显然有以下 2 2 2 种方案:[ A D AD AD B C BC BC],[ A B AB AB C D CD CD].
再考虑一下 6 6 6 个点的情形

在这里插入图片描述
其实由于连线不会相互交叉,每一次连线,相当于在饼上切了一刀,将饼分为两部分,我们可以照着操作一下。
首先假设我们第一刀切 B E BE BE,那么这块饼分成了两部分,一部分上还有4个点可供切割,另一部分0个点可供切割,而由于 B E BE BE 已经被切过,这两个点接下来应该不计入考虑,事实上我们把问题分解成了两部分,即圆上4个点和圆上0个点的情形.
在这里插入图片描述
这一种情形下的切法数为 2 × 1 = 2 2\times 1=2 2×1=2 种.
如果第一步沿 E C EC EC 或者 E D ED ED 切,则连不满 N N N 根线.
如果第一步沿 E A EA EA 切,则切法仍为 2 × 1 = 2 2\times 1=2 2×1=2 种.
如果第一步沿 E F EF EF 切,显然只剩下 1 1 1 种.
显然我们已经算完了所有不重复的结果,因此 6 6 6 个点时有 5 5 5 种方法.

一般情况

那么如果有 2 N 2N 2N 个点呢?
根据以上我们得出,如果第一刀,使得分成的两半上均只有奇数个点,将连不满 N N N 根线,换言之,此时满足要求的切法为 0 0 0.
假设,圆上有 2 k 2k 2k 个点时,切法为 f ( k ) f(k) f(k),那么根据第一刀分成的左右两边的点的个数,由加法原理以及乘法原理可以得到
f ( N + 1 ) = f ( 0 ) f ( N ) + f ( 1 ) f ( N − 1 ) + ⋯ + f ( N ) f ( 0 ) . f(N+1)=f(0)f(N)+f(1)f(N-1)+\dotsm+f(N)f(0). f(N+1)=f(0)f(N)+f(1)f(N1)++f(N)f(0).
这里注意,一旦点的分布确定了,第一刀左侧为 N N N,右侧为 0 0 0和左侧为 0 0 0,右侧为 N N N 将指向不同的结果,看我们之前对于 6 6 6 个点的情况分析,我们先取定一个点作为初始点,事实上这个点是任意选的,假定点的编号从小到大顺时针排序,连 A 1 A 2 A_1A_2 A1A2 即为左 N N N 0 0 0,连 A 1 A N + 1 A_1A_{N+1} A1AN+1 则相反. 可以看出,前者的组合里面,无论哪一种,都不可能含有 A 1 A N + 1 A_1A_{N+1} A1AN+1,而后者的组合也都不可能含有 A 1 A 2 A_1A_2 A1A2,因此这两种方案是不同的.

那么既然初始点的选择是任意的,我们是否需要为此乘上 2 N 2N 2N 或者 N N N 之类的系数呢?

我们还是回到 6 6 6 个点的分析中去,倘若我们选择 B C BC BC 作为第一刀,结果又将如何呢?答案是结果完全一样,无论是数量还是切法,都将保持一致.

我们以集合的角度进行思考,一种切法中的所有弦构成一个集合,所有的切法集合构成全集 U U U,所以其实定弦是一种遍历的方式,首先圆上所有的点都要被连到,那也就意味着所有的切法里面必然含有所有点,那么如果我们选定一个有特征的点 A A A,作为起始点,这个起始点是非特异的,而作为 A A A 这个点而言,和周围的点也只有 N N N 种连接方式,我们考虑了这 N N N 种连接方式的全体,相当于是对这个集合做了一次遍历,既然是遍历,也即意味着选一个点即可计算出全体,而不是一种特例,所以不用乘以任何系数。

所以本道题的答案就是第 N N N 个卡特兰数. 贴上AC代码吧。

#include<iostream>  
#include<cstring>
using namespace std;  
int main() {  
    unsigned long long ctl[32768], i, j, k, n;  
    memset(ctl, 0, sizeof(ctl));  
    ctl[0] = ctl[1] = 1;ctl[2] = 2;  
    cin >> n;  
    for (i = 3; i <= n; ++i)  
        for (j = 0; j < i; ++j) {  
            ctl[i] += ctl[j] * ctl[i - j - 1];  
            ctl[i] %= 100000007;  
        }  
    cout << ctl[n];  
    return 0;  
}  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值