题意; 给你n,m,1<=n<=100000,1<=m<=10^9,要你计算T(n)模m的值。其中T(n)定义为:∑Fi,1<=i<=n,Fi为卡特兰数。
解题思路:首先注意到n的范围为1~100000,比较大,肯定不能对和中的每一个卡特兰数模m的值一个个直接求。于是自然
想到卡特兰数的递推公式 : ,但是递推公式里面的系数含有除法,于是在对m取模的时候,想到求n+1的乘法逆元。但是这里n+1和m不一定互素。于是我们可以将Fn看成两部分的乘积,一部分是与m互素的部分,另一部分是不与m互素的部分。为了达到这个目的,我们可以先将m分解质因素,得到m中包含的所有不同的素因子,然后在递推求解时,将4*i-2和i+1中与m互素的部分单独拿出来,形成a/b的形式,再将与m不互素的素因子拿出来上下约分后得到各素因子的个数加到一个用于记录的数组中去,形成Fi中与m不互素的部分。而对于a/b来说,可以乘上前一个卡特兰数与m互质的部分,再乘上b的逆元得到当前卡特兰数与m互质的部分,更新记录变量值。将此变量的值乘上数组中的各素因子然后取模便得到当前卡特兰数模m的值。求完每一个后,相加取模即可。
代码:
#include <stdio.h>
#include <string.h>
int mprime[40],keep[40];
int flag[40000],prime[40000],count;
void get_prime()
{
int i,j;
count = 0;
memset(flag,0,sizeof(flag));
for(i = 2;i*i<40000;i++)
if(flag[i]==0)
for(j = i*i;j<40000;j+=i)
flag[j] = 1;
for(i = 2;i<40000;i++)
if(flag[i]==0)
prime[count++] = i;
}
__int64 extgcd(__int64 a,__int64 b,__int64 &x,__int64 &y)
{
__int64 gcd,temp;
if(b==0)
{
x = 1;y = 0;
return a;
}
gcd = extgcd(b,a%b,x,y);
temp = x;
x = y;
y = temp-(a/b)*y;
return gcd;
}
__int64 powermod(__int64 n,__int64 x,__int64 m)
{
__int64 res=1;
for(;x;x>>=1)
{
if(x&1)
res = (res*n)%m;
n = (n*n)%m;
}
return res;
}
int main()
{
__int64 sum,keep1,t,x,y,m,temp,n;
int c,i,j,k;
get_prime();
while(scanf("%I64d%I64d",&n,&m)&&n+m!=0)
{
if(m==1)
{
printf("0\n");
continue;
}
c = 0;
temp = m;
for(i = 0;i<count&&temp>1;i++)
{
if(temp%prime[i]==0)
{
mprime[c++] = prime[i];
while(temp%prime[i]==0)
temp/=prime[i];
}
}
if(temp>1)
mprime[c++] = temp;
memset(keep,0,sizeof(keep));
sum = 1;keep1 = 1;
for(i = 2;i<=n;i++)
{
t = 4*i-2;
for(j = 0;j<c;j++)
{
while(t%mprime[j]==0)
{
t/=mprime[j];
keep[j]++;
}
}
keep1=(keep1*t)%m;
t = i+1;
for(j = 0;j<c;j++)
{
while(t%mprime[j]==0)
{
t/=mprime[j];
keep[j]--;
}
}
if(t>1)
{
extgcd(t,m,x,y);
x = (x%m+m)%m;
keep1 = (keep1*x)%m;
}
t = keep1;
for(j = 0;j<c;j++)
if(keep[j]>0)
t = (t*powermod((__int64)mprime[j],(__int64)keep[j],m))%m;
sum=(sum+t)%m;
}
printf("%I64d\n",sum);
}
return 0;
}