通过费马小定理求组合数(防止越界,因为做除法之前不能够取余)

转载自:http://blog.csdn.net/xuezhongfenfei/article/details/10100651

费马小定理:a^(p-1) ≡1(mod p)a和p是互质的(p为质数)。 那么对于任意的求a^m%p则可以转换为a^(m%(p-1))%p,可以使复杂度很高的求此方的进行化简

欧拉函数上的应用就是:  a,m互质,a^φ(m)≡1(mod m)//(φ(m)为欧拉函数,表示的是m之前与m互质的正整数的个数,而如果m为质数的话则m-1就为m的欧拉函数值,这又到了费马小定理)

求排列组合中的C(n,m),如果n和m都很大,而且取得的值是要模上mod,那么如果仍然按照n!/(m!*(n-m)!)算的话肯定就会爆,如果边去摸边算的话结果就会不对,那么应该怎么算呢?

转换为n!*(m!)^(md-2)*((n-m)!^mod-2);mod-2也可以为mod的欧拉函数值-1,这里暂且设mod为质数

因为求的是n!/(m!*(n-m)!),则可以转换为n!*(m!)^(-1)*(n-m)!^-1,

n!*m!^(-1)*(n-m)!^(-1) *m!^(mod-1) *(n-m)!^(mod-1),因为后两项都为1,所以乘后结果不变,然后就转换为了上面的式子,这样就不用担心除法的时候因为取模的问题了!


有一道例题,hdu4869 ,第一场多校第九题。记录一下。

/***********************************************\
 |Author: Messyidea
 |Created Time: 2014-7-22 13:48:33
 |File Name: b.cpp
 |Description: 
\***********************************************/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#define L(rt) (rt<<1)
#define R(rt) (rt<<1|1)
#define mset(l,n) memset(l,n,sizeof(l))
#define rep(i,n) for(int i=0;i<n;++i)
#define maxx(a) memset(a, 0x3f, sizeof(a))
#define zero(a) memset(a, 0, sizeof(a))
#define srep(i,n) for(int i = 1;i <= n;i ++)
#define MP make_pair
const int inf=0x3f3f3f3f ;
const double eps=1e-8 ;
const double pi=acos (-1.0);
typedef long long ll;
#define mod 1000000009
#define LL long long 
using namespace std;
int n,m;
ll f[100005];
LL extend_gcd(LL a,LL b,LL &x,LL &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    LL gcd=extend_gcd(b,a%b,x,y);
    LL t=x;
    x=y;
    y=t-a/b*x;
    return gcd;
}
LL Get_Inverse(LL num)
{
    LL x,y;
    extend_gcd(num,mod,x,y);
    return (x%mod+mod)%mod;
}
LL Combine(LL n,LL m)//计算组合数C(n,m)
{
    LL t1=1,t2=1;
    for(LL i=n;i>m;i--)
    {
        t1=(t1*i)%mod;
        t2=(t2*(i-m))%mod;
    }
    return t1*Get_Inverse(t2)%mod;
}

void pre(){
    int i;
    f[0] = 1;
    for(int i=1;i<100005;++i){
        f[i] = (f[i-1]*i)%mod;
    }
}

int ma[100005];
int da[100005];
int l,r;
ll quickmod(ll x,ll y){
    ll res = 1;
    while(y>0){
        if(y&1) res = res * x % mod;
        x = x * x % mod;
        y>>=1;
    }
    return res;
}
void solve(){
    l = r = m;
    int num = 0;
    int p;
    if(m % 2 == 0) {
        p = 0;
    } else {
        p = 1;
    }
    rep(i,n){
        int tl = l,tr = r;
        if(ma[i] % 2 == 1) p = p ^ 1;
        num = 0;
        if(m - tl >= ma[i]) da[num ++] = tl + ma[i];
        if(tl >= ma[i]) da[num++] = tl - ma[i];
        if(m - tl < ma[i]){
            da[num ++] = m-(ma[i] - (m-tl));
        }
        if(tl < ma[i]) {
            da[num ++] = ma[i] - tl;
        }
        if(m - tr >= ma[i]) da[num ++] = tr + ma[i];
        if(tr >= ma[i]) da[num++] = tr - ma[i];
        if(m - tr < ma[i]){
            da[num ++] = m-(ma[i] - (m-tr));
        }
        if(tr < ma[i]) {
            da[num ++] = ma[i] - tr;
        }
        if(tr >= ma[i] && tl <= ma[i]){
            if(p == 1) da[num ++] = 1;
            else da[num ++] = 0;
        }
        if(tr + ma[i] >= m && tl + ma[i] <= m ){
            if(p == 1){
                if(m % 2 == 0) da[num++] = m-1;
                else da[num ++] = m;
            } else {
                if(m % 2 == 0) da[num ++] = m;
                else da[num ++] = m-1;
            }
        }
        sort(da,da+num);
        //rep(i,num) cout<<da[i]<<" ";cout<<endl;
        tl = da[0];tr = da[num-1];
        l = tl;r = tr;
    }
    LL ans = 0;
    ans = 0;
    LL tp = Combine(m,l);
    //cout<<l<<" "<<r<<endl;
    for(int i = l;i <= r;i += 2){
        ans += f[m]*quickmod(f[m-i],mod-2)%mod*quickmod(f[i],mod-2)%mod;
        ans %= mod;
    }
    printf("%lld\n",ans);
}
int main() {
    //freopen("input.txt","r",stdin); 
    pre();
    while(~scanf("%d%d",&n,&m)){
        rep(i,n) scanf("%d",&ma[i]);
        solve();
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值