HDU4828(2014百度之星初赛)求乘法逆元&&Catalan数

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4828

Catalan 数:前几项为 : 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, ...

原理:

令h(0)=1,h(1)=1,catalan数满足递推式 [1]
h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)h(0) (n>=2)
例如:h(2)=h(0)*h(1)+h(1)*h(0)=1*1+1*1=2
h(3)=h(0)*h(2)+h(1)*h(1)+h(2)*h(0)=1*2+1*1+2*1=5
另类递推式 [2]
h(n)=h(n-1)*(4*n-2)/(n+1);
递推关系的解为:
h(n)=C(2n,n)/(n+1) (n=0,1,2,...)
递推关系的另类解为:
h(n)=c(2n,n)-c(2n,n+1)(n=0,1,2,...)
逆元  即 逆元素 。
在 数学里,逆元素广义化了 加法中的 加法逆元和 乘法中的 倒数。直观地说,它是一个可以取消另一给定元素运算的元素。
扩展的欧几里德算法求乘法逆元   
转载自: http://zhulinb123.blog.163.com/blog/static/184414043201142195442392/

扩展欧几里得的应用:
模P乘法逆元
 对于整数a、p,如果存在整数b,满足ab mod p =1,则说,b是a的模p乘法逆元。
 定理:a存在模p的乘法逆元的充要条件是gcd(a,p) = 1

 证明:
 首先证明充分性
 如果gcd(a,p) = 1,根据欧拉定理,aφ(p) ≡ 1 mod p,因此
 显然aφ(p)-1 mod p是a的模p乘法逆元。

 再证明必要性
 假设存在a模p的乘法逆元为b
 ab ≡ 1 mod p
 则ab = kp +1 ,所以1 = ab - kp
 因为gcd(a,p) = d
 所以d | 1
 所以d只能为1 
扩展欧几里德算法对于最大公约数的计算和普通欧几里德算法是一致的。计算乘法逆元则显得很难明白。下面是证明:

 首先重复拙作整除中的一个论断:

 如果gcd(a,b)=d,则存在m,n,使得d = ma + nb,称呼这种关系为a、b组合整数d,m,n称为组合系数。当d=1时,有 ma + nb = 1 ,此时可以看出m是a模b的乘法逆元,n是b模a的乘法逆元。 
(按照上面算出来的m,n可能为负数,最后要把它转化为正数)。完整程序如下:*/
#include <iostream>
using namespace std;
int extended_gcd(int a,int b, int &x, int &y)
{
    if (b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    else
    {
        int gcd = extended_gcd(b, a % b, x, y);
        int t = x;
        x = y;
        y = t - (a / b) * y;
        return gcd;
    }
}
int main()
{
    int i, x, y;
    const int P = 13;
    for (i = 1; i < P; ++i)
    {
        extended_gcd(i, P, x, y);
        while (x < 0) x += P;
        printf("1 div %d = %d\n", i, x);
    }
    return 0;
}
//扩展的欧几里德算法求乘法逆元
#include <stdio.h>
int ExtendedEuclid(int f, int d, int *result);
int main() {
    int x, y, z;
    z = 0;
    printf("输入两个数:\n");
    scanf("%d%d", &x, &y);
    if (ExtendedEuclid(x, y, &z))
        printf("%d和%d互素,乘法的逆元是:%d\n", x, y, z);
    else
        printf("%d和%d不互素,最大公约数为:%d\n", x, y, z);
    return 0;
}

int ExtendedEuclid(int f, int d, int *result) {
    int x1, x2, x3, y1, y2, y3, t1, t2, t3, q;
    x1 = y2 = 1;
    x2 = y1 = 0;
    x3 = (f >= d) ? f : d;
    y3 = (f >= d) ? d : f;
    while (1) {
        if (y3 == 0) {
            *result = x3; /* 两个数不互素则result为两个数的最大公约数,此时返回值为零 */
            return 0;
        }
        if (y3 == 1) {
            *result = y2; /* 两个数互素则resutl为其乘法逆元,此时返回值为1 */
            return 1;
        }
        q = x3 / y3;
        t1 = x1 - q*y1;
        t2 = x2 - q*y2;
        t3 = x3 - q*y3;
        x1 = y1;
        x2 = y2;
        x3 = y3;
        y1 = t1;
        y2 = t2;
        y3 = t3;
    }

}

打表得到前几项 判断为Catalan 数 但是求Catalan数的过程中有除法 需要求逆元
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAX 1000003
#define MOD 1000000007
#define LL long long
using namespace std;
 
LL s[MAX];

LL exgcd(LL a,LL b,LL &x,LL &y){
     if(b==0){
         x=1;
         y=0;
         return a;
     }
     LL d = exgcd(b,a%b,x,y);
     LL t=x;
     x=y;
     y=t-a/b*y;
     return d;
} 
void deal(){
     s[1]=s[2]=1;
     int n = MAX;
     LL x,y;
     for(int i=2;i<=n;i++){
         s[i+1]=(4*i-6)%MOD;
         s[i+1]=(s[i+1]*s[i])%MOD;
         exgcd(i,MOD,x,y);
         s[i+1]=(s[i+1]*((x+MOD)%MOD))%MOD;
    }
} 
int main()
{
     int t,n;
     deal();
     scanf("%d",&t);
     for(int z=1;z<=t;z++){
         scanf("%d",&n);
         printf("Case #%d:\n",z);
         printf("%I64d\n",s[n+2]);
     }
     return 0;
}

代码如下




你可能感兴趣的:(HDU4828(2014百度之星初赛)求乘法逆元&&Catalan数)