关于卡特兰数:
- 定义:对于一个由n个1,和0组成的序列,并且满足每一位之前的0的个数必定大于等于1的个数。这些序列的总数我们称之为卡特兰数。
- 建模 (图片来源于https://www.acwing.com/solution/content/46130/)
我们将0表示为向左走,1表示为向右走。
通过图中我们可以发现 终点(n,n)一共有C(2n,n)种走法。
而违规路线经过红线反转必然经过点(n-1,n+1),那么违规路线的总数即为C(2n,n-1),那么合法的路线就为C(2n,n)-C(2n,n-1)。
引题:TZOJ 4838: 栈
这道题其实就是卡特兰数的应用,你有没有发现先将入栈操作标记为0,出栈操作标记为1,那么一些数经过入栈出栈操作后的序列的总和即为卡特兰数。
本题数据范围小,只用开long long 或用dp硬切就可以完成。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL C(int a,int b)
{
LL res=1;
for(int i=1,j=a;i<=b;i++,j--)
{
res=res*j/i;
}
//cout<<res<<endl;
return res;
}
int main()
{
int n;
cin>>n;
cout<<C(2*n,n)-C(2*n,n-1)<<endl;
}
题目描述:
对于本道题,思路与前面介绍的卡特兰数一样,唯一需要注意的就是
高精度了。其中我们对求阶乘进行了分解质因数的优化。
#include<bits/stdc++.h>
using namespace std;
const int N = 10005;
int prime[N],f[N],cnt;
int a[N],b[N];
void init()
{
for(int i=2;i<N;i++)
{
if(!f[i])
{
prime[++cnt]=i;
}
for(int j=1;j<=cnt&&i*prime[j]<N;j++)
{
f[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
}
int get(int n,int p)//对于在n!中质因子p的个数。
{
int s=0;
while (n)
{
s+=n/p;n/=p;
}
return s;
}
void mul(int r[],int &len,int p)
{
int t=0;
for(int i=0;i<len;i++)
{
t+=r[i]*p;
r[i]=t%10;
t/=10;
}
while(t)
{
r[len++]=t%10;
t/=10;
}
}
int C(int x,int y,int r[])
{
int len=1;
r[0]=1;
for(int i=1;i<=cnt;i++)
{
int p=prime[i];
int s=get(x,p)-get(y,p)-get(x-y,p);
while(s--)mul(r,len,p);
}
return len;
}
void sub(int a[],int a1,int b[],int b1)
{
for(int i=0,t=0;i<a1;i++)
{
a[i]-=t+b[i];
if(a[i]<0)
{
a[i]+=10;t=1;
}
else t=0;
}
}
int main()
{
init();
int n,m;
scanf("%d%d", &n, &m);
int a1=C(n+m,m,a);
int b1=C(n+m,m-1,b);
sub(a,a1,b,b1);
int k=a1-1;
while(!a[k])k--;
while(k>=0)cout<<a[k--];
cout<<endl;
}