问题描述
一列火车n节车厢,依次编号为1,2,3,…,n。
每节车厢有两种运动方式,进栈与出栈,问n节车厢出栈的可能排列方式有多少种。
输入格式
输入一个整数n,代表火车的车厢数。
输出格式
输出一个整数s表示n节车厢出栈的可能排列方式数量。
数据范围
1≤n≤60000
样例
输入样例:
3
输出样例:
5
思路
- 如果需要求具体方案数, 就需要用到dfs搜索, 用三个状态去转换
- 对于合法方案, 如果1代表出栈,0代表进栈,此时合法方案序列就满足Catalan数列
- 求组合数时, 我们用到阶乘分解,求出分子分母的质约数,然后化简,把因数相乘就是最后结果
- 由于最后结果很大,我们用到高精度乘法, 在高精度乘法时,用压位的方法优化(数据太严格了, O(N)的算法都会卡掉)
代码
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
using namespace std;
typedef long long ll;
const int N= 120010;
int prime[N], power[N];
int n, cnt;
bool vis[N];
void get_primes(int n)
{
for(int i = 2; i <= n; i++)
{
if(!vis[i]) prime[++cnt] = i;
for(int j = 1; i * prime[j] <= n; j++)
{
vis[prime[j] * i] = true;
if(i % prime[j] == 0) break;
}
}
}
vector<ll> mul(vector<ll> a, int b)
{
vector<ll> ans;
ll t = 0;
for(int i = 0; i < a.size(); i++)
{
t += a[i] * b;
ans.push_back(t % 1000000000);
t /= 1000000000;
}
while(t)
{
ans.push_back(t % 1000000000);
t /= 1000000000;
}
return ans;
}
void out(vector<ll> ans)
{
for(int i = ans.size() - 1; i >= 0; i--)
if(i != ans.size() - 1)
printf("%09lld", ans[i]);
else printf("%lld", ans[i]);
}
void get_factor(int n, int flag)
{
for(int i = 1; i <= cnt; i++)
{
int s = 0;
int p = prime[i];
int t = n;
while(t)
{
s += t / p;
t /= p;
}
if(flag)
power[p] += s;
else
power[p] -= s;
}
}
int main()
{
scanf("%d", &n);
get_primes(n * 2);
get_factor(n * 2, 1);
get_factor(n, 0);
get_factor(n, 0);
int t = n + 1;
for(int i = 1; i <= cnt && t != 0; i++)
{
int p = prime[i];
while(t % p == 0)
{
t /= p;
power[p]--;
}
}
vector<ll> ans;
ans.push_back(1);
for(int i = 1; i <= cnt; i++)
{
int p = prime[i];
if(power[p] != 0)
{
for(int j = 0; j < power[p]; j++)
ans = mul(ans, p);
}
}
out(ans);
return 0;
}