扩展的欧几里得算法求逆元 hdu-3240-Counting Binary Trees

题目链接:

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;
}




你可能感兴趣的:(扩展的欧几里得算法求逆元 hdu-3240-Counting Binary Trees)