您的位置:首页 > 其它

hdu 3240 卡特兰数与拓展欧几里得求逆元

2014-08-25 19:37 323 查看
这道题目做了好久。。。。果然数论的东西还是博大精深

首先是如何看出这是一个卡特兰数。对于一个t(n)来讲,划分子问题便是

先取一个点作为根节点,设它的左儿子有x个点,则他的右儿子有n-1-x个点。用一个dp
表示有多少种方法

则 dp
= dp[x]+dp[n-1-x];x=(1,n-1)的整数,但是数据范围是100000所以dp要超时。

不过通过观察发现这是一个卡特兰数,所以就有了dp
= dp[n-1]*(4*n-2)/(n+1)

但是还没有结束,接下来是取模的问题。对于取模如果与前面的数互质非常简单可以通过一次拓展欧几里得直接得到逆元。

但是m不一定互质,每一次要通过分子分母约分,详情自己看代码好了。。

#include<iostream>

#include<cstring>

#include<cstdio>

using namespace std;

void exgcd(int a,int b,int &x,int &y)

{

if(b==0)

{

x=1;

y=0;

}

else

{

int t;

exgcd(b,a%b,x,y);

t=x; x=y;

y=t-(a/b)*y;

}

}

long long int c[100005];

int a,m;

int cnt = 1,p[1005],l[1005];

void CallOne(long long &ans,int x){

for(int i=1;i<cnt;i++){

while(x%p[i]==0){

x = x / p[i];

l[i]++;

}

}

ans = (ans*x)%m;

}

void CallTwo(long long &ans,int x){

for(int i=1;i<cnt;i++){

while(x%p[i]==0&&l[i]>0){

l[i]--;

x = x/p[i];

}

}

if(x>1){

int ni,tem;

exgcd(x,m,ni,tem);

ans = ((ans*ni)%m+m)%m;

}

}

void Get_prime(int MAX_N){

for(int i=2;i*i<=MAX_N;i++)

if(MAX_N%i==0)

{

p[cnt++]=i;

while(MAX_N%i==0)

MAX_N/=i;

}

if(MAX_N>1)

p[cnt++]=MAX_N;

}

int main(){

long long ans = 1,res;

while(cin >> a >> m&&(a+m)){

cnt = 1;res = 1;

memset(l,0,sizeof(l));

Get_prime(m);

c[0] = 1;c[1] = 1;

ans = 1;

long long tem;

for(int i=2;i<=a;i++){

CallOne(ans,4*i-2);

CallTwo(ans,i+1);

tem = ans;

for(int k=1;k<cnt;k++){

for(int kt=1;kt<=l[k];kt++){

tem = (tem*p[k])%m;

}

}

res += tem;

res%=m;

}

printf("%I64d\n",res);

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: