您的位置:首页 > 其它

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

2014-07-24 00:04 288 查看
转载自: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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐