博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
『火车进出栈问题 卡特兰数』
阅读量:6759 次
发布时间:2019-06-26

本文共 4080 字,大约阅读时间需要 13 分钟。


火车进出栈问题

Description

一列火车n节车厢,依次编号为1,2,3,…,n。每节车厢有两种运动方式,进栈与出栈,问n节车厢出栈的可能排列方式有多少种。

Input Format

一个数,n(n<=65000)

Output Format

一个数s表示n节车厢出栈的可能排列方式

Sample Input

3

Sample Output

5

解析

转换一下题意:求卡特兰数的第\(n\)项,不取模。

先考虑\(n\)小一点的做法,我们可以直接用卡特兰数的线性递推公式:

\[f_n=f_{n-1}\frac{4n-2}{n+1}\]
然后直接敲高精度乘除就可以得到\(52\)分的高分啦。

然后考虑一下优化:我们发现数字位数超级多,可以写一个压位高精,这样又可以多得\(20-30\)分。

然后再想一想正解,我们要换一个思路,应该用这个公式:

\[f_n=\frac{C_{2n}^n}{n+1}\]
这是卡特兰数的通项公式,我们可以利用组合数的定义式简单的推导一下。
\[⇒f_n=\frac{!(2n)/(!n)^2}{n+1} \\ \ \\⇒f_n=\frac{!(2n)}{(!n)^2(n+1)}\]

看一下算数基本定理:

\[A=p_1^{a_1}*p_2^{a_2}*...*p_k^{a_k}\]

注意到卡特兰数每一项都一定是一个整数,也就是说,如果将如上公式的分子分母分别分解质因数,那么分母的各个因式指数上一定是可以被分子的各个因式相减抵消的。

这样,高精度除法就不用写了,这是一个巨大的突破。

但是分子分母都是存在阶乘结构的,那么就考虑然后对某个数的阶乘分解质因数。

对于\(!n=1*2*...*n\),考虑他存在多少个质因子\(p\)

  • 显然有\(\lfloor\frac{n}{p}\rfloor\)个数有至少一个因子\(p\)
  • 那么,有\(\lfloor\frac{n}{p^2}\rfloor\)个数有至少两个因子\(p\)
  • \(...\)
  • 依次类推,有\(\lfloor\frac{n}{p^x}\rfloor\)个数有至少\(x\)个因子\(p\)

代码片段:将\(!n\)分解质因数

\(Code:\)

//prime[i]存了1-n范围内的所有质数for(int i=1;i<=cnt;i++){    if(prime[i]>n)break;    int sum=0;    for(int j=prime[i];j<=n;j*=prime[i])        sum+=n/j;    p[++tot]=prime[i];a[tot]=sum;}

那么我们就能分解质因数了。先线性筛法预处理\(2n\)范围内的所有质数,然后循环枚举每一个质因子,对于每一个质因子,我们借助如上规律累乘质因子\(p\),再累加每一个\(\lfloor\frac{2n}{p^x}\rfloor\)即可得到因子\(p\)\(!(2n)\)的分解式中的指数大小,从而得到\(!(2n)\)的分解式。

同理,我们可以继续用这种方法分解\((!n)^2\),再将相同因子的质数减去即可。

对于\(n+1\),我们直接暴力分解一下,将相同因子的质数减去,就得到了最终答案的分解式,然后快速幂配合高精度乘法做一下就可以了。

\(Code:\)

#include
using namespace std;const int base=1e8,Maxlen=1e4;struct bign{ int d[Maxlen],len; inline void clear(void) { len=0; memset(d,0,sizeof d); } inline void print(void) { printf("%d",d[len]); for(int i=len-1;i>=1;i--) printf("%08d",d[i]); } bign operator = (int a) { clear(); do { d[++len]=a%base; a/=base; } while(a); return *this; } bign operator * (bign a) { bign res; res.clear(); long long temp; for(int i=1;i<=len;i++) { temp=0; for(int j=1;j<=a.len;j++) { temp+=1LL*d[i]*a.d[j]+res.d[i+j-1]; res.d[i+j-1]=temp%base; temp/=base; } if(temp) res.d[i+a.len]=temp; } res.len=len+a.len; while(!res.d[res.len]&&res.len>1)res.len--; return res; }};//结构体封装的大整数高精度const int N=65000+20;int n,vis[N*2],prime[N*2],cnt,p[N*2],a[N*2],reto[N*2],tot;inline void Sieve(void){ //线性筛法 for(int i=2;i<=2*n;i++) { if(!vis[i])prime[++cnt]=i; for(int j=1;j<=cnt&&i*prime[j]<=n*2;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0)break; } }}inline long long power(long long a,long long b){ //快速幂 long long res=1; while(b) { if(1&b)res*=a; a*=a;b>>=1; } return res;}inline void solve(void){ //先分解!(2n) for(int i=1;i<=cnt;i++) { if(prime[i]>2*n)break; int sum=0; for(int j=prime[i];j<=2*n;j*=prime[i]) sum+=(2*n)/j; p[++tot]=prime[i];a[tot]=sum; //p存底数,a存指数,tot为下标 reto[prime[i]]=tot; //记录一下,对于质因数prime[i],它在分解式中对应的下标为reto[prime[i]] } //分解!n for(int i=1;i<=cnt;i++) { if(prime[i]>n)break; int sum=0; for(int j=prime[i];j<=n;j*=prime[i]) sum+=n/j; //!n有两个,将指数一起减去 a[reto[prime[i]]]-=(2*sum); } //暴力分解一下n+1 int m=n+1; for(int i=1;i<=cnt;i++) { if(prime[i]>m)break; if(m%prime[i]==0) { int sum=0; while(m%prime[i]==0) { sum++; m/=prime[i]; } //将指数减去 a[reto[prime[i]]]-=sum; } } bign ans,temp; ans=1; for(int i=1;i<=tot;i++) { //将分解式乘起来就是答案 temp=power(p[i],a[i]); ans=ans*temp; } ans.print();}int main(void){ freopen("easy.in","r",stdin); freopen("easy.out","w",stdout); scanf("%d",&n); Sieve(); solve(); return 0;}


转载于:https://www.cnblogs.com/Parsnip/p/10525854.html

你可能感兴趣的文章
NPOI workbook.RemoveSheetAt(0); 删除sheet页 次序 sheettmpRequire.CopySheet("Require", true);...
查看>>
Go标准库:深入剖析Go template
查看>>
ant design pro (四)新增页面
查看>>
uni - 使用npm
查看>>
ASP.NET Core多语言 (转载)
查看>>
java中比较两个double类型值的大小
查看>>
golang ----gc问题
查看>>
WPF去除边框的方法
查看>>
浅析NTFS 文件系统数据流安全问题
查看>>
Smart Device Framework 2.2 发布了
查看>>
Humble Numbers soj1029
查看>>
程序员技术练级攻略
查看>>
ls只显示文件名/只显示文件夹名
查看>>
Java并发编程:同步容器
查看>>
水晶报表之动态列--简化版实现
查看>>
验证控件的使用四( RangeValidator)
查看>>
网络编程(一):用C#下载网络文件的2种方法
查看>>
复制graphic
查看>>
基于NopCommerce的开源电商系统改造总结
查看>>
JavaScript是怎样让互联网变慢的(及对策)
查看>>