1. 程式人生 > >Lucas定理(求組合數取模) 擴充套件Lucas定理(解決模數非質情況)

Lucas定理(求組合數取模) 擴充套件Lucas定理(解決模數非質情況)

在比賽時 , 如果遇到Cnm的n比較大 , 我們不能通過預處理階乘和逆元來計算 , 而題目又要求對答案取一個質數模的時候 , 我們可以用Lucas定理來簡化計算

Lucas 定理:

定義 : n,m是非負整數,p是素數時 , Lucas(n,m)=Cnm%p

公式 :

Lucas(n,m)=Cn%pm%pLucas(n/p,m/p)%p

程式碼 :

LL Lucas(LL n,LL m){
    if(m==0)return 1ll;
    return C(n%mod
,m%mod)*Lucas(n/mod,m/mod)%mod; }

擴充套件Lucas定理:

擴充套件Lucas定理用於解決模數非質數時的情況

懶得敲了,直接手寫上圖好了(好久沒寫作業不會寫字了多多包涵)
這裡寫圖片描述

這裡寫圖片描述

也就是說 , 我們對於模數M , 在非質數的時候 , 把它分成多個兩兩互質的數(按照質因子分解一定保證兩兩互質) , 求出C%piqiai , 然後把多組piqiai求CRT就是答案

接下來要解決的是C%piqi

C實際上的三個階乘的乘除 , 所以當下解決的就變成了n!%piqi

假設為
19!%32


19!=1234567819
=[124578161719](369121518)
=[124578161719]36(123456)

1. 後半部分也是階乘 , 直接重新遞迴即可
2. 前半部分中 , 有一個迴圈節 , [124578]%9=[(1+9k)(2+9k)(4+9k)(5+9k)(7+9k)(8+9k)]%9 , 對於後面的零頭暴力就行了
3. 對於中間p的此法 , 記錄一下數量 , 最後三個階乘的數量相加減即可

模板 :

#include<bits/stdc++.h>
using namespace std;
#define i64 long long


i64 POW(i64 a,i64 b,i64 mod)
{
    i64 ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}

i64 POW(i64 a,i64 b)
{
    i64 ans=1;
    while(b)
    {
        if(b&1) ans=ans*a;
        a=a*a;
        b>>=1;
    }
    return ans;
}


i64 exGcd(i64 a,i64 b,i64 &x,i64 &y)
{
    i64 t,d;
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    d=exGcd(b,a%b,x,y);
    t=x;
    x=y;
    y=t-a/b*y;
    return d;
}

bool modular(i64 a[],i64 m[],i64 k)
{
    i64 d,t,c,x,y,i;

    for(i=2;i<=k;i++)
    {
        d=exGcd(m[1],m[i],x,y);
        c=a[i]-a[1];
        if(c%d) return false;
        t=m[i]/d;
        x=(c/d*x%t+t)%t;
        a[1]=m[1]*x+a[1];
        m[1]=m[1]*m[i]/d;
    }
    return true;
}



i64 reverse(i64 a,i64 b)
{
    i64 x,y;
    exGcd(a,b,x,y);
    return (x%b+b)%b;
}

i64 C(i64 n,i64 m,i64 mod)
{
    if(m>n) return 0;
    i64 ans=1,i,a,b;
    for(i=1;i<=m;i++)
    {
        a=(n+1-i)%mod;
        b=reverse(i%mod,mod);
        ans=ans*a%mod*b%mod;
    }
    return ans;
}

i64 C1(i64 n,i64 m,i64 mod)
{
    if(m==0) return 1;
    return C(n%mod,m%mod,mod)*C1(n/mod,m/mod,mod)%mod;
}

i64 cal(i64 n,i64 p,i64 t)
{
    if(!n) return 1;
    i64 x=POW(p,t),i,y=n/x,temp=1;
    for(i=1;i<=x;i++) if(i%p) temp=temp*i%x;
    i64 ans=POW(temp,y,x);
    for(i=y*x+1;i<=n;i++) if(i%p) ans=ans*i%x;
    return ans*cal(n/p,p,t)%x;
}

i64 C2(i64 n,i64 m,i64 p,i64 t)
{
    i64 x=POW(p,t);
    i64 a,b,c,ap=0,bp=0,cp=0,temp;
    for(temp=n;temp;temp/=p) ap+=temp/p;
    for(temp=m;temp;temp/=p) bp+=temp/p;
    for(temp=n-m;temp;temp/=p) cp+=temp/p;
    ap=ap-bp-cp;
    i64 ans=POW(p,ap,x);
    a=cal(n,p,t);
    b=cal(m,p,t);
    c=cal(n-m,p,t);
    ans=ans*a%x*reverse(b,x)%x*reverse(c,x)%x;
    return ans;
}

//計算C(n,m)%mod
i64 Lucas(i64 n,i64 m,i64 mod)
{
    i64 i,t,cnt=0;
    i64 A[205],M[205];
    for(i=2;i*i<=mod;i++) if(mod%i==0)
    {
        t=0;
        while(mod%i==0)
        {
            t++;
            mod/=i;
        }
        M[++cnt]=POW(i,t);
        if(t==1) A[cnt]=C1(n,m,i);
        else A[cnt]=C2(n,m,i,t);
    }
    if(mod>1)
    {
        M[++cnt]=mod;
        A[cnt]=C1(n,m,mod);
    }
    modular(A,M,cnt);
    return A[1];
}


int main(){
    i64 n,m,mod;
    while(scanf("%lld%lld%lld",&n,&m,&mod))
    printf("%lld\n",Lucas(n,m,mod));
}

相關推薦

Lucas定理(合數) 擴充套件Lucas定理(解決情況)

在比賽時 , 如果遇到CmnCnm的n比較大 , 我們不能通過預處理階乘和逆元來計算 , 而題目又要求對答案取一個質數模的時候 , 我們可以用Lucas定理來簡化計算 Lucas 定理: 定義 : n,m是非負整數,p是素數時 , Lucas(

合數(楊輝三角打表 & 逆元(擴充套件歐幾里得、費馬小定理、尤拉定理、線性求法) & Lucas

    在acm競賽中,組合數取模的題目還是經常會見到的,所以這是有必要掌握的一個演算法。我本人就因為這個東西而被坑了很多次了= =之前的部落格也都扯過了,就不多說了,下面進入正題。 (1)楊輝三角求組合數     楊輝三角這個東西應該都不陌生,三角的兩邊始終為一,之後向

Lucas定理合數

引入 楊輝三角 std 數據 組合數取模 有關 ans main include 引入: 組合數C(m,n)表示在m個不同的元素中取出n個元素(不要求有序),產生的方案數。定義式:C(m,n)=m!/(n!*(m-n)!)(並不會使用LaTex QAQ)。 根據題目中對組合

Lucas定理合數

對於C(n, m) mod p。這裡的n,m,p(p為素數)都很大的情況。就不能再用C(n, m) = C(n - 1,m) + C(n - 1, m - 1)的公式遞推了。 這裡用到Lusac定理 For non-negative integers m and n and a prime p, the f

Lucas定理——大合數

大組合數取模,求C[n][m]%p 公式:C[n][m]%p == C[n%p][m%p]*C[n/p][m/p]%p 注意,Lucas的要求是n,m<=10^5,如果n,m>=10^5,那麼要求p<=10^5 楊輝三角: f[0][

Lucas定理合數模板

end code == turn tdi div rac bsp 模板 $Lucas(n,m,p)=C(n\%p,m\%p)*Lucas(n/p,m/p,p)$ $C^n_m=\frac{n!}{m!(n-m)!}$ $x^{p-1}\equiv 1(mod p)\Long

Lucas定理合數

#include <iostream> #include <cstdio> #include <cstring> #include <algorithm>

合數&&Lucas定理題集

pac 假設 次方 href ace 範圍 統一 lucas定理 != 題集鏈接: https://cn.vjudge.net/contest/231988 解題之前請先了解組合數取模和Lucas定理 A : FZU-2020 輸出組合數C(n, m) mod p (

合數(楊輝三角+Lucas定理+合數

/* (1) 1 <= m <= n <= 1000 和 1 <= p <= 10^9 ( p可以是任何數 ) 這個問題比較簡單,組合數的計算可以靠 楊輝

Lucas定理應用分析——大合數

    首先給出Lucas(盧卡斯)定理:     有非負整數A、B,和素數p,A、B寫成p進製為:A=a[n]a[n-1]...a[0],B=b[n]b[n-1]...b[0]。 則組合數C(A,B)與C(a[n],b[n])×C(a[n-1],b[n-1])×...×C

hdu5968(合數Lucas定理)

瞬間移動 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Sub

3037 Saving Beans (數論,合數lucas定理

也是通過看別人的程式碼才知道這個題應該怎麼做:Lucas定理題目相當於求n個數的和不超過m的方案數。如果和恰好等於m,那麼就等價於方程x1+x2+...+xn = m的解的個數,利用插板法可以得到方案數為:(m+1)*(m+2)...(m+n-1)  = C(m+n-1,n-1) = C(m+n-1,m)現在

hdu-3037-合數-Lucas定理

http://acm.hdu.edu.cn/showproblem.php?pid=3037 題意很簡單 求C(n+m,m)%p,P是小於1e5的素數 n,m《1e18 那麼得到    

hdu3037 lucas 定理 合數

#include<stdio.h> #include<string> #include<map> #include<vector> #include&l

hdu 3037 費馬小定理+逆元合數+Lucas定理

void log 打表 數學 mod turn ret iostream toc 組合數學推推推最後,推得要求C(n+m,m)%p 其中n,m小於10^9,p小於1^5 用Lucas定理求(Lucas定理求nm較大時的組合數) 因為p數據較小可以直接階乘打表求逆元

合數1:盧卡斯定理

模板: #include<iostream> #include<algorithm> #include<cstdio> #define ll long long #define N 100005 using namespace std; int k,n,m

組合數以及合數

1、採用C(a, b) = n! / (m! * (n - m)!),適用範圍為n <= 20 typedef long long ll; const int maxn=20+5; ll a[maxn]; void init() { a[0]=1; for(int i=1; i&l

[演算法 18_001] Lucas 定理與大合數

Lucas 定理 該定理是用來求當 (nm) ( n m

簡單合數(除法

#include<bits/stdc++.h> using namespace std; #define inf 0x3f3f3f3f const int maxn=1e5+9; #define LL long long int e_gcd(int a,int b

各種逆元求法 合數 comb (合數 Lucas

組合數取模(comb) 【問題描述】 計算C(m,n)mod 9901的值 【輸入格式】 從檔案comb.in中輸入資料。 輸入的第一行包含兩個整數,m和n 【輸出格式】 輸出到檔案comb.out中。 輸出一行,一個整數 【樣例輸入】 2