预处理逆元 快速求组合数
#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define ull unsigned long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const int N=2e5+10;
const ll P=1e9+7;
ll read(){
ll s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * f;
}
using namespace std;
ll qpow(ll a,ll x){
ll ans=1;
while(x){
if(x&1){
ans=(ans*a)%P;
}
a=(a*a)%P;
x>>=1;
}
return ans;
}
ll Fac[N]; // 阶乘
ll inv[N]; // 逆元
void prepare(int n){
Fac[0]=1;inv[0]=1;
Fac[1]=inv[1]=1;
for(int i=2;i<=n;i++){
Fac[i]=(Fac[i-1]*i)%P;
inv[i]=(inv[i-1]*qpow(i,P-2))%P;
}
return ;
}
ll C(int u,int v){
if(u<0 or v<0 or u<v) return 0;
// printf("%d %d %d",Fac[u],inv[v],inv[u-v]);
return (Fac[u]*inv[v])%P * inv[u-v]%P;
}
void solve(){
prepare(10);
printf("%d\n",C(5,3));
printf("%d\n",C(5,1));
printf("%d\n",C(5,0));
}
int main (){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
solve();
return 0;
}
组合数取模(模板)
初入数论的萌新
分享一下我的组合数取模的想法.
感悟:
1.运用费马小定理
2.分数如何取余结论:a/b%p=[a*b^(p-2)]%p;**
3. lucas定理:*C(n,m)%p=C(n/p,m/p)C(n%p,m%p);
4.c(n,m) 结果必定是n的整数倍。
主要针对p是较小的数.
p必须是素数,p必须是素数,p必须是素数(说三遍)
费马小定理如下:(待会组合数取余用得上的)
这里是引用费马小定理: p必须是整数
( (x)^p ) % p=(x) % p;
(x^p)%p=(x)%p; -----1式
1式可推出:
(x^(p-1))%p=1; /这个后文要用到的喔,记住他。。。。
这么推出的呢?? 来看看
eg: (2^13)%13==2;
//怎么理解推导式呢? 2^13 和2有同样的地位,所以等式两边除以2,就得到的推倒式。 我是这么理解的。
推导式—>(2^12)%13=1;
//再推倒可以
C(n,m)%p=C(n/p,m/p)*C(n%p,m%p);
可能读到这里就很奇怪,一下子抛出这么定理,公式干啥呢??
额。。。。先记住,用着吧。以后再慢慢研究吧。
组合数的是阶乘除以阶乘,eg:C(10,3)=(10* 9* 8)/(1* 2* 3);
对。。组合数是用分数的形式表现出来的(通过约分肯定是整数)。
那么我们就来看看是如何对分数取余:
假如我们现在已经知道了一个分数:999/9
假设p=1e9+7;(再次强调p一定要是素数!!!!)。
那么 999/9%p=111; ----1式
根据费马小定理: 9^(p-1)%p=1; —2式
1和2式相乘 得到:[999*(9^(p-2))]%p=111; —3式
1式由分数转换成了整数,结果也是相同的。
所以蛮 999/9%p=[999*(9^(p-2))]%p;
结论:a/b%p=[a*b^(p-2)]%p;
神奇吧。。。。。。
求解:C(n,m) 并对结果取余(p=1e9+7).
代码如下:
#include<iostream>
using namespace std;
#define ll long long
const ll mod=1e9+7;
ll read(){
ll s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * f;
}
ll quick_mod(ll a,ll b){ ///快速幂,a^b(a的b次方)
ll ans=1;
while(b){
if(b&1){
ans=(ans*a)%mod;
}
b>>=1;
a=(a*a)%mod;
}
return ans;
}
ll Comb(ll n,ll m){ //求组合数C(n,m)
// n,m 都是p取余后得到的数,即是n,m<mod
ll a=1,b=1; //a/b;
if(m==0) return 1;
if(n<m) return 0;
if(n-m<m) m=n-m;
ll sum=1;
for(int i=1;i<=m;i++){
a=a*(n+i-m)%mod;
b=b*i%mod;
}
sum=(a*quick_mod(b,mod-2))%mod;
return sum;
}
ll lucas(ll n,ll m){ //运用lucas定理
//C(n,m)%p=C(n/p,m/p)*C(n%p,m%p);
//有点像是p的进制的转换(哈哈哈,其实就是的)
ll sum=1;
while(n&&m){
sum=(sum*Comb(n%mod,m%mod))%mod;
n/=mod;
m/=mod;
}
return sum;
}
int main (){
ll n,m;
n=read(); m=read();
cout<<lucas(n,m);
}