W Z K 打 雪 仗 WZK打雪仗 WZK打雪仗
题目链接:jzoj 1997
题目大意
在一个环上有n*2个点,我们要求出有多少种连法可以用n条线连接成n对点,且线不会交叉。
样例输入
5
样例输出
42
样例解释
一种可行的方案如下:
数据范围
对于30%数据: n<=30。
对于100%数据: n<=100。
思路
这道题其实就是一道卡特兰数。
卡特兰数的递推公式为:
S
(
n
)
=
S
(
0
)
×
S
(
n
−
1
)
+
S
(
1
)
×
S
(
n
−
2
)
+
.
.
.
+
S
(
n
−
1
)
×
S
(
0
)
S(n)=S(0)×S(n-1)+S(1)×S(n-2) +...+S(n-1)×S(0)
S(n)=S(0)×S(n−1)+S(1)×S(n−2)+...+S(n−1)×S(0)
不过这道题的数挺大的,要用到高精加和高精乘。
我们利用公式dp一波,就可以求出答案了。
代码
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
ll n,f[101][11],temp[11],tt[11];
void cheng(ll x,ll y)//高精乘
{
memset(temp,0,sizeof(temp));
for (int i=1;i<=10;i++)
for (int j=1;j<=10;j++)
{
temp[i+j-1]+=f[x][i]*f[y][j];
temp[i+j]+=temp[i+j-1]/1000000000;
temp[i+j-1]%=1000000000;
}
}
void jia(ll x)//高精加
{
memset(tt,0,sizeof(tt));
for (ll i=1;i<=10;i++)
{
tt[i]=tt[i]+f[x][i]+temp[i];
tt[i+1]+=tt[i]/1000000000;
tt[i]%=1000000000;
}
for (ll i=1;i<=10;i++)
f[x][i]=tt[i];
}
int main()
{
// freopen("war.in","r",stdin);
// freopen("war.out","w",stdout);
scanf("%lld",&n);//读入
f[0][1]=1;f[1][1]=1;//初始化
for (ll i=2;i<=n;i++)
for (ll j=0;j<=i-1;j++)
{
cheng(j,i-j-1);
jia(i);//递推
}
ll ans=10;
while (!f[n][ans]&&ans>0) ans--;//去除前导零
printf("%lld",f[n][ans]);//输出
for (ll i=ans-1;i>=1;i--) printf("%0*lld",9,f[n][i]);//输出
// fclose(stdin);
// fclose(stdout);
return 0;
}