[HNOI2009]有趣的数列,洛谷P3200,Catalan+简化公式

版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢 https://blog.csdn.net/Deep_Kevin/article/details/83385516

正题

      题目传送门

      因为所有奇数项是升序的,所以a_1<a_3<...<a_{2n-1}

      又因为奇数项小于偶数项a_1<a_3<...<a_{2n-1}<a_{2n}

      所以每个偶数位都能插在所对应奇数项的后面,换一种说法,就是把2*n个数从小到大排好序,把奇数项和偶数项丢进去,使得每一个前缀,奇数项的个数都比偶数项多。

      求Catalan数有4个公式。(点链接查看)

      其中有两个组合数公式是在一起的。

      那么前面两种很明显不行,第一种会超时,第二种因为p不是质数,求不了逆元,而且p也没有什么很好的性质。

      那么组合数,为了简单,我们选取第一种,并且化简

      \\Catalan(n)=\frac{C(2n,n)}{n+1} \\=\frac{\frac {\prod_{n+1}^{2n}} {\prod_{1}^n} }{n+1} \\=\frac {\prod_{n+2}^{2n}} {\prod_{1}^n}

      很明显这个东西肯定是可以整除的。

      所以,又不能求逆元,那么我们就分解质因数,然后上下抵消不就行了?

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

int n,p;
int prime[2000010],mmin[2000010];
int t[2000010];
bool vis[2000010];

long long ksm(long long x,int t){
	long long tot=1;
	while(t>=1){
		if(t%2==1){
			tot*=x;
			tot%=p;
		}
		x*=x;
		x%=p;
		t/=2;
	}
	return tot;
}

int main(){
	scanf("%d %d",&n,&p);
	for(int i=2;i<=2*n;i++){
		if(vis[i]==false) prime[++prime[0]]=i,mmin[i]=i;
		for(int j=1;j<=prime[0] && i*prime[j]<=2*n;j++){
			vis[i*prime[j]]=true;
			mmin[i*prime[j]]=prime[j];
			if(i%prime[j]==0) break;
		}
	}
	int x;
	for(int i=n+2;i<=2*n;i++){
		x=i;
		while(x!=1) t[mmin[x]]++,x/=mmin[x];
	}
	for(int i=2;i<=n;i++){
		x=i;
		while(x!=1) t[mmin[x]]--,x/=mmin[x];
	}
	long long ans=1;
	for(int i=1;i<=prime[0];i++){
		ans=ans*ksm((long long)prime[i],t[prime[i]])%p;
	}
	printf("%lld\n",ans);
}

      

      

猜你喜欢

转载自blog.csdn.net/Deep_Kevin/article/details/83385516