题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=3240
题目意思:
求不超过n个节点的不同2叉树的种数。最终结果模m.
解题思路:
容易发现这是个卡特兰数求和的问题。
h[n]=h[n-1]*(4*n-2)/(n+1)
但是m是随机输入的,不一定为质数,这是本题的难点之处,把h[n-1]*(4*n-2)/(n+1)分成两部分,一部分与m互质,一部分整体考虑,是否含m的质因子。
如果与m互质,则一定不含m的任何质因数。将(n+1)含有m的质因数的排除掉,(4×n-2)含有m的质因数的加进去,整体考虑m的所有质因数。
互质的部分,用拓展的欧几里得算法直接求逆元,不互质的部分直接乘进去。
代码:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF (1<<30)
#define PI acos(-1.0)
#define ll __int64
using namespace std;
/*
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
*/
int pp[120],nup[120];
int cnt,n,m;
void egcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1,y=0;
return ;
}
egcd(b,a%b,x,y);
int t=x;
x=y,y=t-a/b*y;
return ;
}
int main()
{
while(scanf("%d%d",&n,&m)&&(n+m))
{
if(m==1)
{
puts("0");
continue;
}
int tt=m;
cnt=0;
for(int i=2;i*i<=tt;i++) //将m的质因数给分离出来
{
if(tt%i==0)
pp[++cnt]=i;
while(tt%i==0)
tt/=i;
}
if(tt>1)
pp[++cnt]=tt;
memset(nup,0,sizeof(nup)); //看成是整体来考虑
ll res=1,ans=1; //i=1时
for(int i=2;i<=n;i++)
{
int t=4*i-2;
for(int j=1;j<=cnt;j++) //将分子的m的质因数加进去
{
while(t%pp[j]==0)
{
nup[j]++;
t/=pp[j];
}
}
res=(res*t)%m; //把不包含m的质数的部分保存
t=i+1;
for(int j=1;j<=cnt;j++) //将分子中含有m的质因数的部分去掉
{
while(t%pp[j]==0) //整体考虑,一定会整除的
{
nup[j]--;
t/=pp[j];
}
}
if(t>1) //这一部分与m互质的,等于1就不用考虑了
{
int x,y;
egcd(t,m,x,y);
x=x%m;
if(x<0)
x+=m;
res=(res*x)%m; //保存
}
ll temp=res;
for(int k=1;k<=cnt;k++) //注意加全面,包括两部分
{
for(int j=1;j<=nup[k];j++)
temp=(temp*pp[k])%m;
}
ans=(ans+temp)%m;
}
printf("%I64d\n",ans);
}
return 0;
}