题目描述如下
一列火车 n节车厢,依次编号为 1,2,3,…,n。
每节车厢有两种运动方式,进栈与出栈,问 n 节车厢出栈的可能排列方式有多少种。
输入格式
输入一个整数 n,代表火车的车厢数。
输出格式
输出一个整数 s表示 n 节车厢出栈的可能排列方式数量。
数据范围
1≤n≤60000
题目思路
很明显这是一个栈相关问题,每节车可以进栈出栈一次。因为栈中车的节书不能小于0,所以在做出抉择时进栈数必须大于出栈数。又因为最终总的进出栈数相等所以很自热的可以联想到卡特兰数。(具体讲解可看acwing y总的视频)最终即要求(Cn 2n)/n + 1。题目的数据较大而且时间卡的很死。这就需要很大的优化来算了。
首先这里使用了除法,而上下的数都很大我们可以考虑类似约分的思路。将上下分成一个个相乘的质数后约掉。将上面剩下的质数再相乘。
所使用的技巧有
数组的高精度乘法
压位(用于高精度乘法的时间优化)
求埃氏法质数
求上下中每个质数的数量
代码如下
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 120010;
const int M = 1e9;
LL res[N], tt;
int prime[N];
bool is_prime[N];
int get(int n,int p){
int s = 0;
while(n){
s += n / p;
n /= p;
}
return s;
}
void mult(int n){
LL t = 0;
for(int i = 0;i <= tt;i ++){
res[i] = res[i] * n + t;
t = res[i] / M;
res[i] %= M;
}
while(t){
res[++ tt] = t % M;
t /= M;
}
}
void out(){
printf("%lld",res[tt]);
for(int i = tt - 1;i >= 0;i --) printf("%09lld",res[i]);
cout << endl;
}
int main(){
int n;
cin >> n;
for(int i = 2;i <= 2 * n;i ++)
for(int j = i + i;j <= 2 * n;j += i)
is_prime[j] = true;
for(int i = 2;i <= 2 * n;i ++)
if(!is_prime[i]) prime[i] = get(2 * n, i) - get(n, i) * 2;
int k = n + 1;
for(int i = 2;i <= 2 * n;i ++)
while(k % i == 0){
k /= i;
prime[i] --;
}
res[0] = 1;
for(int i = 2;i <= 2 * n;i ++)
while(prime[i] --) mult(i);
out();
return 0;
}