HDU 3240

求卡特兰数前N项的和模M。

直接求必定是不可能的,卡特兰数太大了。想了好久,本打算把位数拆成素数相乘,然后记录下各素数的个数计算。可惜,TLE。。。。因为N太大了。

除法必定是要用到逆元的,但分母与M不一定互质。M拆成素数相乘形式,记录下各个素数在数组PRIME。于是,可以把4*i-2和i+1拆成素数相乘,若在PRIME中,则必定是与M不互质的,只能将个数记在NUM中,4*i-2的+1,i+1的-1。那么,把各素数约去后的i-1剩下的必与M互质。于是就可以和M求逆元的。

可以看程序,很容易懂。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define LL __int64
#define NP 30000
using namespace std;

LL prime[NP];
int num[NP],np;

void exgcd(__int64 a,__int64 b,__int64 &x,__int64 &y){
    if(b==0){
        x=1; y=0;
        return ;
    }
    exgcd(b,a%b,x,y);
    __int64 t=x;
    x=y;
    y=t-a/b*y;
}


void cal1(LL &ans,LL tp,LL &m){
	for(int i=0;i<np&&prime[i]<=tp;i++){
		if(tp%prime[i]==0){
			while(tp%prime[i]==0){
				num[i]++;
				tp/=prime[i];
			}
		}
	}
	ans=(ans*tp)%m;
}

void cal2(LL &ans,LL tp,LL &m){
	LL x,y;
	for(int i=0;i<np&&prime[i]<=tp;i++){
		if(tp%prime[i]==0){
			while(tp%prime[i]==0){
				num[i]--;
				tp/=prime[i];
			}
		}
	}
	exgcd(tp,m,x,y);
	ans=(ans*((x%m+m)%m))%m;
}

int main(){
	LL n,m;
	while(scanf("%I64d%I64d",&n,&m),n||m){
		LL tp=m; np=0;
		for(LL i=2;i*i<=tp;i++){
			if(tp%i==0){
				prime[np++]=i;
				while(tp%i==0)
				tp/=i;
			}
		}
		if(tp>1)
		prime[np++]=tp;
		memset(num,0,sizeof(num));
		LL ans=1,res=1,tmp;
		for(LL i=2;i<=n;i++){
			cal1(ans,4*i-2,m);
			cal2(ans,i+1,m);
			tmp=ans;
			for(int k=0;k<np;k++)
				if(num[k])
				for(int p=0;p<num[k];p++)
				tmp=(tmp*prime[k])%m;
			res=(res+tmp)%m;
		}
		printf("%I64d\n",res);
	}
	return 0;
}

  

posted @ 2014-10-10 23:21  chenjunjie1994  阅读(375)  评论(0编辑  收藏  举报