蛋糕(卡特兰数)
题目描述
输入格式
一行两个整数 n 和 P, 意义如题面所示。
输出格式
一行一个整数, 表示有多少种切法。
输入样例
6 1000000007
输出样例
【样例一输出】
14
题解:
我们用f[i-2]来表示将凸i边形划分的方案数,那么我们设一个凸n边形的顶点顺时针顺序依次为p1,p2……pn,显然p1和pn相邻。现在我们选点pk,(k不为1和n),那么p1,pn,pk会把图形分成两部分+p1pnpk这个三角形,特殊的,当k=n-1或k=2时,我们把它看成分成了一个n-1边形和一个0边形。又因为0边形无法划分,我们暂定f[0]=1(表示0边形的划分数)。且由常识可知f[1]=1。
现在我们就可以得出递推式:f[i-2]=f[0]*f[i-3]+f[1]*f[i-4]+……+f[i-3]*f[0];由此可观察出这就是我们所熟悉的卡特兰数,求n边形的划分方案,就是求卡特兰数的第n-2项。
在这里先介绍一下卡特兰数的数学递推式:
h(n)=C(2n,n)/(n+1) h(n)=h(n-1)*(4*n-2)/(n+1); h(n)=c(2n,n)-c(2n,n-1)
接下来就好简单啦,不就求第n-2项吗?
等等,真的简单吗?对于本题,n<=10,000,000,而且mod的p还不是质数!!!
假如我们质因数分解,用最小表示法处理f[n-2],时间复杂度高达O(nlogn),所以我们必须找更优的方法。
我们的最终目的是用最小表示法表示f[n-2],那么我们只需知道每个质数出现的个数就ok了。比如质数2,1-n中出现2的有n/2次,出现2^2的有n/4次,……,所以质数2出现的次数为
n/2+n/4+……+n/2^m,所以贴代码(这个需要自己理解一下):
#include<algorithm> #include<fstream> #include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> using namespace std; int n,p,tot,pri[10000010],vis[20000020]; long long mi[10000010]; long long ans; void Make_pri() { for (int i=2; i<=2*n; i++) { if (vis[i]==0) {tot++; pri[tot] = i;} for (int j=1; j<=tot; j++) { long long x = (long long)pri[j]*(long long)i; if (x>2*n) break; vis[x] = 1; if (i%pri[j]==0) break; } } } void Add(int len,int cur) { for (int i=1; i<=tot; i++) { long long k = pri[i]; while (k<=len) { mi[i]+=(long long)cur*(long long)(len/k); k = k*pri[i]; } } } void Fast_mi() { for (int i=1; i<=tot; i++) { long long a = pri[i],b = mi[i]; while (b>0) { if (b%2==1) ans = ((long long)ans*(long long)a)%p; b = b/2; a = ((long long)a*(long long)a)%p; } } } int main() { freopen("c.in","r",stdin); freopen("c.out","w",stdout); scanf("%d%d",&n,&p); Make_pri(); n = n-2; Add(2*n,1); Add(n,-1); Add(n+1,-1); ans = 1; Fast_mi(); printf("%I64d\n",ans); return 0; }