fzu 1775 Counting Binary Trees 卡特兰数取模求和

题意; 给你n,m,1<=n<=100000,1<=m<=10^9,要你计算T(n)模m的值。其中T(n)定义为:∑Fi,1<=i<=n,Fi为卡特兰数。

 

解题思路:首先注意到n的范围为1~100000,比较大,肯定不能对和中的每一个卡特兰数模m的值一个个直接求。于是自然

                  想到卡特兰数的递推公式 :  fzu 1775 Counting Binary Trees  卡特兰数取模求和 - wutyyzchangde - wutyyzchangde 的博客,但是递推公式里面的系数含有除法,于是在对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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值