题目链接:点击这里
%yxc
有 个数,就需要进栈 次,出栈 次,共 次操作
为了描述方便,我们定义符号 为进栈,符号 为出栈。那么,不同的合法的车厢出站序列对应着不同的 序列,
以 为例:
于是,要求车厢出栈的可能排列方式有多少种,问题就转换为, 个 和 个 能组成多少个合法的序列。
这里说的合法是有含义的,例如: 合法、 不合法。
- 肯定是合法的
-
一定要保证当前栈非空,也就是,对每个
来说,任何前缀中的
。
满足这一核心的就是卡特兰数。
个 和 个 组成的总方案数,等价于从 个位置中选出 个 的方案数,即 。
易知, - 不合法方案数 = 合法方案数
下面用非降路径来求解不合法方案数:
首先定义 为向右走, 为向上走。
那么,合法路径的特点就是:任意时刻向右走的距离一定大于等于向上走的距离,即 ,整个路径一定在直线 的下方,由于有 个 和 个 ,其终点一定在 。
非法路径的特点: ,即 ,其终点也在 。
如果将非法路径与直线 第一个交点后面的部分关于直线 轴对称,可以得到一个结论:任何一条非法路径都对应着一条从 到 的路径。
这样一转换,非法路径数量就很好求了:没有限制地从 到 的路径数,等价于 个 和 个 组成的方案数,等价于 从 个位置中选出 个 的方案数,即
最终合法的答案就是 ,代入公式展开,推导结果为
数据 过大,考虑高精度,超时。(看着y总debug也学到了不少)
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;
void multi(vector<int> &a, int b) //高精度乘
{
int t = 0;
for(int i = 0; i < a.size(); i++)
{
a[i] = a[i] * b + t;
t = a[i] / 10;
a[i] %= 10;
}
while(t)
{
a.push_back(t%10);
t /= 10;
}
}
void div(vector<int> &a, int b) //高精度除
{
int t = 0;
for(int i = a.size() - 1; i >= 0; i--) //倒序除
{
a[i] += t*10;
t = a[i] % b;
a[i] /= b;
}
while(a.size() > 1 && a.back() == 0) //删掉首位零
a.pop_back();
}
void out(vector<int> &a) //out方便debug
{
for(int i = a.size() - 1; i >= 0; i--)
printf("%d", a[i]);
printf("\n");
}
int main()
{
int n;
scanf("%d", &n);
vector<int> res;
res.push_back(1);
for(int i = 2 * n, j = 1; j <= n; i--, j++)
{
multi(res, i);
div(res, j);
}
div(res, n+1);
out(res);
return 0;
}
vector换数组、高精度压9位,超时。
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 6000010;
ll res[N], tt;
void multi(int b) //高精度乘
{
ll t = 0;
for(int i = 0; i <= tt; i++)
{
res[i] = res[i] * b + t;
t = res[i] / 1000000000;
res[i] %= 1000000000;
}
while(t)
{
res[++tt] = t % 1000000000;
t /= 1000000000;
}
}
void div(int b) //高精度除
{
ll t = 0;
for(int i = tt; i >= 0; i--) //倒序除
{
res[i] += t*1000000000;
t = res[i] % b;
res[i] /= b;
}
while(!res[tt]) //删掉首位零
tt--;
}
void out() //out方便debug
{
printf("%lld", res[tt]);
for(int i = tt - 1; i >= 0; i--)
printf("%09lld", res[i]);
printf("\n");
}
int main()
{
int n;
scanf("%d", &n);
tt = 0;
res[0] = 1;
for(int i = 2 * n, j = 1; j <= n; i--, j++)
{
multi(i);
div(j);
}
div(n+1);
out();
return 0;
}
组合数 ,对分子分母分解质因数,例如
一个小技巧, 里有多少个因子 : + + + … …,直到 大于 。
AC代码:
#include <iostream>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 6000010, M = 120010;
LL res[N], tt;
int q[M];
bool st[M];
void multi(int b) //高精度乘
{
LL t = 0;
for (int i = 0; i <= tt; i ++ )
{
res[i] = res[i] * b + t;
t = res[i] / 1000000000;
res[i] %= 1000000000;
}
while (t)
{
res[++tt] = t % 1000000000;
t /= 1000000000;
}
}
void out() //输出
{
printf("%lld", res[tt]);
for (int i = tt - 1; i >= 0; i -- ) printf("%09lld", res[i]);
cout << endl;
}
int get(int n, int p) //求n!中有多少个因子p
{
int s = 0;
while (n) s += n / p, n /= p;
return s;
}
int main()
{
int n;
scanf("%d", &n);
for(int i = 2; i <= 2 * n; i++) //筛出2n以内的素数
for(int j = i + i; j <= 2 * n; j += i)
st[j] = true;
for(int i = 2; i <= n * 2; i++)
{
if(!st[i]) //若i是素数,求其个数
{
q[i] = get(n * 2, i) - get(n, i) * 2;
}
}
int k = n + 1; //还要减去分母n+1对应的因子数
for(int i = 2; i <= k; i++)
{
while(k % i == 0)
{
k /= i;
q[i] -- ;
}
}
res[0] = 1;
for(int i = 2; i <= n * 2; i++)
while(q[i]--)
multi(i);
out();
return 0;
}