CH1102 进出栈序列问题 (线性筛法+Catalan数+高精度乘法+阶乘分解+压位优化)

问题描述
一列火车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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值