题目大意:给定一个序列,序列中的数两两不同,每一步进行两种操作,压栈和弹栈,问可能得到多少序列,输出总数。
n<=1000
考虑到每一次只有两种操作,并且不合法的情况就是弹栈次数多余压栈次数,这个就和括号匹配是一个原理,很显然就是卡特兰数了。
卡特兰数的递推公式:C(2n,n)/(n+1),根据我们的常识判断,当n达到1000时,这个数已经远远超过了LL的范围,所以我们考虑高精,但是如果是每一次都乘并更新进位的话,那么恭喜你:时间空间两开花
那么我们先从C(2n,n)上下手,(2n)!/n!/n!,这就是从n+1乘到2n除以1乘到n,那么由于我们知道除完之后得到的一定是整数,所以除数一定含有被除数的所有质因子,并且比被除数多,我们就可以考虑质因数分解。
我们利用线性筛筛出每一个数的最小质因子,将其分解,并记录每一个质因子出现了多少次,遇到一个新的数就不断将其拆分直到为1为止,最后我们利用高精度乘法将他们乘到一起即可。
最后,附上本题代码:
1 #include<cstdio> 2 #define maxn 4000 3 #define LL long long 4 using namespace std; 5 6 LL n,ans[maxn+5],prime[maxn+5],cnt; 7 LL min_prime[maxn+5],cou_prime[maxn+5]; 8 bool vis[maxn+5],ok; 9 10 int main() 11 { 12 scanf("%lld",&n); 13 for(int i=2; i<=n; i++) 14 { 15 if(vis[i]==0) 16 { 17 prime[++cnt]=i; 18 min_prime[i]=i; 19 } 20 LL temp=i; 21 while(min_prime[temp]!=temp) 22 { 23 cou_prime[min_prime[temp]]--; 24 temp/=min_prime[temp]; 25 } 26 cou_prime[min_prime[temp]]--; 27 for(int j=1; j<=cnt&&i*prime[j]<=n*2; j++) 28 { 29 vis[i*prime[j]]=1; 30 min_prime[i*prime[j]]=prime[j]; 31 if(i%prime[j]==0) 32 { 33 break; 34 } 35 } 36 } 37 /*for(int i=1;i<=n;i++) 38 { 39 printf("%lld",cou_prime[i]); 40 }*/ 41 for(int i=n+2; i<=n*2; i++) 42 { 43 if(vis[i]==0) 44 { 45 prime[++cnt]=i; 46 min_prime[i]=i; 47 } 48 LL temp=i; 49 while(min_prime[temp]!=temp) 50 { 51 cou_prime[min_prime[temp]]++; 52 temp/=min_prime[temp]; 53 } 54 cou_prime[min_prime[temp]]++; 55 for(int j=1; j<=cnt&&i*prime[j]<=n*2; j++) 56 { 57 vis[i*prime[j]]=1; 58 min_prime[i*prime[j]]=prime[j]; 59 if(i%prime[j]==0) 60 { 61 break; 62 } 63 } 64 } 65 /*for(int i=1; i<=n*2; i++) 66 { 67 printf("%lld",cou_prime[i]); 68 }*/ 69 ans[1]=1; 70 for(int i=1; i<=n*2; i++) 71 { 72 for(int j=1; j<=cou_prime[i]; j++) 73 { 74 for(int k=1; k<=2000; k++) 75 { 76 ans[k]*=i; 77 } 78 for(int k=1; k<=2000; k++) 79 { 80 ans[k+1]+=ans[k]/10; 81 ans[k]%=10; 82 } 83 } 84 } 85 for(int i=2000; i>=1; i--) 86 { 87 if(ans[i]!=0) 88 { 89 ok=1; 90 } 91 if(ok==1) 92 { 93 printf("%lld",ans[i]); 94 } 95 } 96 return 0; 97 }