求组合数(数论)

求组合数Ⅰ

在这里插入图片描述

题解

基于数据范围,我们可以先把相应数据初始化出来。
存在如下公式:
C a b = C a − 1 b + C a − 1 b − 1 C_a^b=C_{a-1}^b+C_{a-1}^{b-1} Cab=Ca1b+Ca1b1
a a a个人中选 b b b个人出来,可以分为包含小明和不包含小明的情况。
包含小明: C a − 1 b − 1 C_{a-1}^{b-1} Ca1b1;不包含小明: C a − 1 b C_{a-1}^b Ca1b
相当于Dp状态转移的过程

import java.io.*;

public class Main{
    
    
    static int N=2010;
    static int mod=(int)(1e9+7);
    static int[][] c=new int[N][N];
    public static void main(String[] args)throws IOException{
    
    
        BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
        int n=Integer.parseInt(in.readLine());
        init();
        String[] strs;
        for(int i=0;i<n;i++){
    
    
            strs=in.readLine().split(" ");
            int a=Integer.parseInt(strs[0]);
            int b=Integer.parseInt(strs[1]);
            System.out.println(c[a][b]);
        }
    }
    static void init(){
    
    
        for(int i=0;i<N;i++){
    
    
            for(int j=0;j<=i;j++){
    
    
                if(j==0)c[i][j]=1; //从i个中选0个有1种方案
                else c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
            }
        }
    }
}

求组合数Ⅱ

在这里插入图片描述

题解

基于本题 a , b a,b a,b的数据范围, 1 ≤ b ≤ a ≤ 1 0 5 1 \leq b \leq a \leq 10^5 1ba105,我们可以先把相应数据初始化出来。
用如下公式将数据提前初始化:
C a b = a ! ( b − a ) ! b ! C_a^b=\frac{a!}{(b-a)!b!} Cab=(ba)!b!a!
因为 a ! m o d p = 1 m o d p × 2 m o d p × 3 m o d p × . . . × ( a − 1 ) m o d p × a m o d p a! mod p=1modp \times 2modp \times 3modp \times ... \times (a-1)modp \times amodp a!modp=1modp×2modp×3modp×...×(a1)modp×amodp
但是 a ! m o d p b ! m o d p ≠ a ! b ! m o d p \frac{a!modp}{b!modp} \not= \frac{a!}{b!}modp b!modpa!modp=b!a!modp
这里可以用逆元来解决这个问题
a ! b ! m o d p = a ! b ! − 1 m o d p \frac{a!}{b!}modp=a!b!^{-1}modp b!a!modp=a!b!1modp

逆元也存在以下情况:
b ! − 1 m o d p = 1 − 1 m o d p × 2 − 1 m o d p × 3 − 1 m o d p × . . . × ( b − 1 ) − 1 m o d p × b − 1 m o d p b!^{-1}modp=1^{-1}modp \times 2^{-1}modp \times 3^{-1}mod p \times ... \times (b-1)^{-1}mod p \times b^{-1}modp b!1modp=11modp×21modp×31modp×...×(b1)1modp×b1modp
证明,假设 a a a x x x的逆元, a x = 1 ax=1 ax=1 b b b y y y的逆元, b y = 1 by=1 by=1,所以 a b x y = 1 abxy=1 abxy=1,所以 a b ab ab x y xy xy的逆元
求逆元可看:
https://blog.csdn.net/m0_54136420/article/details/128311218?spm=1001.2014.3001.5502

import java.io.*;

public class Main{
    
    
    static int N=100010;
    static long[] fact=new long[N];
    static long[] infact=new long[N];
    static int mod=(int)(1e9+7);
    public static void main(String[] args)throws IOException{
    
    
        BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
        
        fact[0]=1;
        infact[0]=1;
        for(int i=1;i<N;i++){
    
    
            fact[i]=fact[i-1]*i%mod;
            infact[i]=infact[i-1]*qmi((long)i,mod-2,mod)%mod;
        }
        
        String[] strs=in.readLine().split(" ");
        int n=Integer.parseInt(strs[0]);
        for(int i=1;i<=n;i++){
    
    
            strs=in.readLine().split(" ");
            int a=Integer.parseInt(strs[0]);
            int b=Integer.parseInt(strs[1]);
            System.out.println(fact[a]*infact[a-b]%mod*infact[b]%mod);
        }
    }
    static long qmi(long a,int k,int p){
    
    
        long res=1;
        while(k!=0){
    
    
            if((k&1)==1)res=(res*a)%p;
            k>>=1;
            a=a*a%p;
        }
        return res;
    }
}

求组合数Ⅲ

在这里插入图片描述

题解

基于本题的数据范围。可以考虑用卢卡斯定理
卢卡斯定理公式:
C a b ≡ C a m o d p b m o d p ⋅ C a / p b / p ( m o d p ) C_a^b \equiv C_{a mod p}^{b mod p} \cdot C_{a/p}^{b/p}(mod p) CabCamodpbmodpCa/pb/p(modp)
证明:
a = a k ⋅ p k + a k − 1 ⋅ p k − 1 + ⋯ + a 0 ⋅ p 0 a=a_k \cdot p^k+a_{k-1} \cdot p^{k-1}+ \cdots+a_0 \cdot p^0 a=akpk+ak1pk1++a0p0
b = b k ⋅ p k + b k − 1 ⋅ p k − 1 + ⋯ + b 0 ⋅ p 0 b=b_k \cdot p^k+b_{k-1} \cdot p^{k-1}+ \cdots+b_0 \cdot p^0 b=bkpk+bk1pk1++b0p0

( 1 + x ) p = C p 0 ⋅ 1 + C p 1 x + C p 2 x 2 + ⋯ + C p p x p (1+x)^p=C_p^0 \cdot 1+C_p^1x+C_p^2x^2+\cdots+C_p^px^p (1+x)p=Cp01+Cp1x+Cp2x2++Cppxp
因为 C p 1 , C p 2 , . . . , C p p − 1 C_p^1,C_p^2,...,C_p^{p-1} Cp1,Cp2,...,Cpp1中分子都含有 p p p,所以mod p都等于0
( 1 + x ) p = C p 0 ⋅ 1 + C p 1 x + C p 2 x 2 + ⋯ + C p p x p ≡ 1 + x p ( m o d p ) (1+x)^p=C_p^0 \cdot 1+C_p^1x+C_p^2x^2+\cdots+C_p^px^p \equiv 1+x^p(mod p) (1+x)p=Cp01+Cp1x+Cp2x2++Cppxp1+xp(modp)

( 1 + x ) a = ( 1 + x ) a 0 ⋅ ( ( 1 + x ) p ) a 1 ⋅ ( ( 1 + x ) p 2 ) a 2 ⋯ ( ( 1 + x ) p k ) a k (1+x)^a=(1+x)^{a_0} \cdot ((1+x)^p)^{a_1} \cdot ((1+x)^{p^2})^{a_2}\cdots ((1+x)^{p^k})^{a_k} (1+x)a=(1+x)a0((1+x)p)a1((1+x)p2)a2((1+x)pk)ak
≡ ( 1 + x ) a 0 ⋅ ( 1 + x p ) a 1 ⋅ ( 1 + x p 2 ) a 2 ⋯ ( 1 + x p k ) a k ( m o d p ) \equiv (1+x)^{a_0} \cdot (1+x^p)^{a_1}\cdot(1+x^{p^2})^{a_2}\cdots(1+x^{p^k})^{a_k}(mod p) (1+x)a0(1+xp)a1(1+xp2)a2(1+xpk)ak(modp)

( 1 + x ) a (1+x)^a (1+x)a中包含 x b x^b xb的常数项为 C a b C_a^b Cab
根据②和③可得,该 ( 1 + x ) a 0 ⋅ ( 1 + x p ) a 1 ⋅ ( 1 + x p 2 ) a 2 ⋯ ( 1 + x p k ) a k (1+x)^{a_0} \cdot (1+x^p)^{a_1}\cdot(1+x^{p^2})^{a_2}\cdots(1+x^{p^k})^{a_k} (1+x)a0(1+xp)a1(1+xp2)a2(1+xpk)ak中包含 x b x^b xb的常数项是 C a k b k ⋅ C a k − 1 b k − 1 ⋯ C a 0 b 0 ( m o d p ) C_{a_k}^{b_k} \cdot C_{a_{k-1}}^{b_{k-1}} \cdots C_{a_0}^{b_0}(mod p) CakbkCak1bk1Ca0b0(modp)

综上 C a b ≡ C a k b k ⋅ C a k − 1 b k − 1 ⋯ C a 0 b 0 ( m o d p ) C_a^b\equiv C_{a_k}^{b_k} \cdot C_{a_{k-1}}^{b_{k-1}} \cdots C_{a_0}^{b_0}(mod p) CabCakbkCak1bk1Ca0b0(modp)
C a b ≡ C a m o d p b m o d p ⋅ C a / p b / p ( m o d p ) C_a^b\equiv C_{a mod p}^{b mod p} \cdot C_{a/p}^{b/p}(mod p) CabCamodpbmodpCa/pb/p(modp)

不清楚 C a b ≡ C a k b k ⋅ C a k − 1 b k − 1 ⋯ C a 0 b 0 ( m o d p ) C_a^b\equiv C_{a_k}^{b_k} \cdot C_{a_{k-1}}^{b_{k-1}} \cdots C_{a_0}^{b_0}(mod p) CabCakbkCak1bk1Ca0b0(modp) C a b ≡ C a m o d p b m o d p ⋅ C a / p b / p ( m o d p ) C_a^b\equiv C_{a mod p}^{b mod p} \cdot C_{a/p}^{b/p}(mod p) CabCamodpbmodpCa/pb/p(modp)为啥相等的,看下面:
根据① a = a k ⋅ p k + a k − 1 ⋅ p k − 1 + ⋯ + a 0 ⋅ p 0 a=a_k \cdot p^k+a_{k-1} \cdot p^{k-1}+ \cdots+a_0 \cdot p^0 a=akpk+ak1pk1++a0p0得, a m o d p = a 0 a mod p=a_0 amodp=a0
同理 b m o d p = b 0 b mod p=b_0 bmodp=b0,所以 C a m o d p b m o d p = C a 0 b 0 C_{a mod p}^{b mod p}=C_{a_0}^{b_0} Camodpbmodp=Ca0b0

然后相当于 C a b = C a 0 b 0 ⋅ C a / p b / p ( m o d p ) C_a^b=C_{a_0}^{b_0}\cdot C_{a/p}^{b/p}(mod p) Cab=Ca0b0Ca/pb/p(modp) C a / p b / p = C a / p m o d p b / p m o d p ⋅ C a / p 2 b / p 2 C_{a/p}^{b/p}=C_{a/pmodp}^{b/pmodp}\cdot C_{a/p^2}^{b/p^2} Ca/pb/p=Ca/pmodpb/pmodpCa/p2b/p2
根据①得 C a / p m o d p b / p m o d p = C a 1 b 1 C_{a/pmodp}^{b/pmodp}=C_{a_1}^{b_1} Ca/pmodpb/pmodp=Ca1b1,所以 C a b = C a 0 b 0 ⋅ C a 1 b 1 ⋅ C a / p 2 b / p 2 C_a^b=C_{a_0}^{b_0}\cdot C_{a_1}^{b_1} \cdot C_{a/p^2}^{b/p^2} Cab=Ca0b0Ca1b1Ca/p2b/p2,不断递归,最后得到:
C a b ≡ C a k b k ⋅ C a k − 1 b k − 1 ⋯ C a 0 b 0 ( m o d p ) C_a^b\equiv C_{a_k}^{b_k} \cdot C_{a_{k-1}}^{b_{k-1}} \cdots C_{a_0}^{b_0}(mod p) CabCakbkCak1bk1Ca0b0(modp)

import java.io.*;

public class Main{
    
    
    static long p;
    public static void main(String[] args)throws IOException{
    
    
        BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
        String[] strs=in.readLine().split(" ");
        int n=Integer.parseInt(strs[0]);
        for(int i=0;i<n;i++){
    
    
            strs=in.readLine().split(" ");
            long a=Long.parseLong(strs[0]);
            long b=Long.parseLong(strs[1]);
            p=Long.parseLong(strs[2]);
            System.out.println(lucas(a,b));
        }
    }
    static long lucas(long a,long b){
    
    
        if(a<p&&b<p)return C(a,b)%p;
        return (C(a%p,b%p)*lucas(a/p,b/p))%p;
    }
    static long C(long a,long b){
    
    
        if(a<b)return 0;
        long res=1;
        for(long i=1,j=a;i<=b;i++,j--){
    
    
            res=res*j%p;
            res=res*qmi(i,p-2)%p;
        }
        return res;
    }
    static long qmi(long a,long k){
    
    
        long res=1;
        while(k!=0){
    
    
            if((k&1)==1)res=res*a%p;
            k>>=1;
            a=a*a%p;
        }
        return res;
    }
}

求组合数Ⅳ

在这里插入图片描述

题解

C a b = a × ( a − 1 ) × . . . × ( a − b + 1 ) b × ( b − 1 ) × . . . × 1 = a × ( a − 1 ) × . . . × ( a − b + 1 ) × ( a − b ) ! b × ( b − 1 ) × . . . × 1 × ( a − b ) ! = a ! b ! ( a − b ) ! 分解质因数 : C a b = p 1 a 1 p 2 a 2 ⋯ p k a k 求 a !分解 p 的次数 s u m [ p ] = ⌊ a p ⌋ + ⌊ a p 2 ⌋ + ⌊ a p 3 ⌋ + ⋯ = p 的倍数在 [ 1 , a ] 中的个数 + p 2 的倍数在 [ 1 , a ] 中的个数 + . . . 比如求 8 ! = 1 × 2 × 3 × 4 × 5 × 6 × 7 × 8 中 2 的个数。 1 到 8 之间 2 的一次方有 2 、 4 、 6 、 8 四个 2 含 2 的二次方有 4 、 8 ,但 4 、 8 中 2 的一次方已经记过一次了 , 所以只加二次方中的两个 2 含 2 的三次方有 8 ,但 8 中的一次、二次方都被记过一次了,所以只加二次方中的一个 2 根据消除质因数, C a b 中 p 的出现次数为: a !中出现的 p 的次数 − ( a − b ) !中出现 p 的次数 − b ! 中 p 出现的次数 \begin{aligned} C_a^b&=\frac{a \times (a-1) \times ... \times (a-b+1)}{b \times (b-1) \times ... \times 1} \\ &= \frac{a \times (a-1) \times ... \times (a-b+1) \times (a-b)!}{b \times (b-1) \times ... \times 1 \times (a-b)!} \\ &= \frac{a!}{b!(a-b)!} \end{aligned} \\ 分解质因数:C_a^b=p_1^{a_1}p_2^{a_2} \cdots p_k^{a_k} \\ \begin{aligned} 求a!分解p的次数sum[p]&=\lfloor \frac{a}{p} \rfloor+\lfloor \frac{a}{p^2} \rfloor+\lfloor \frac{a}{p^3} \rfloor+\cdots \\ &=p的倍数在[1,a]中的个数+p^2的倍数在[1,a]中的个数+... \end{aligned} \\ 比如求8!=1\times 2 \times 3 \times 4 \times 5 \times 6 \times 7 \times 8中2的个数。 \\ 1到8之间2的一次方有2、4、6、8四个2 \\ 含2的二次方有4、8,但4、8中2的一次方已经记过一次了,所以只加二次方中的两个2 \\ 含2的三次方有8,但8中的一次、二次方都被记过一次了,所以只加二次方中的一个2 \\ 根据消除质因数,C_a^b中p的出现次数为:\\ a!中出现的p的次数-(a-b)!中出现p的次数-b!中p出现的次数 Cab=b×(b1)×...×1a×(a1)×...×(ab+1)=b×(b1)×...×1×(ab)!a×(a1)×...×(ab+1)×(ab)!=b!(ab)!a!分解质因数:Cab=p1a1p2a2pkaka!分解p的次数sum[p]=pa+p2a+p3a+=p的倍数在[1,a]中的个数+p2的倍数在[1,a]中的个数+...比如求8=1×2×3×4×5×6×7×82的个数。18之间2的一次方有2468四个22的二次方有48,但482的一次方已经记过一次了,所以只加二次方中的两个22的三次方有8,但8中的一次、二次方都被记过一次了,所以只加二次方中的一个2根据消除质因数,Cabp的出现次数为:a!中出现的p的次数(ab)!中出现p的次数b!p出现的次数
因为没有 m o d mod mod一个大数,所以这里要用高精度相乘,同时还要提前初始化质数。

import java.io.*;
import java.math.BigInteger;

public class Main{
    
    
    static int N=5010;
    static boolean[] st=new boolean[N];
    static int[] primes=new int[N];
    static int cnt;
    static int[] sum=new int[N];
    public static void main(String[] args)throws IOException{
    
    
        BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
        String[] strs=in.readLine().split(" ");
        int a=Integer.parseInt(strs[0]);
        int b=Integer.parseInt(strs[1]);
        get_primes(a);
        
        for(int i=0;i<cnt;i++){
    
    
            int p=primes[i];
            sum[i]=get(a,p)-get(a-b,p)-get(b,p);
        }
        
        BigInteger res=new BigInteger("1");
        for(int i=0;i<cnt;i++){
    
    
            int p=primes[i];
            for(int j=0;j<sum[i];j++){
    
    
                res=res.multiply(new BigInteger(String.valueOf(p)));
            }
        }
        System.out.println(res);
    }
    static void get_primes(int n){
    
    
        for(int i=2;i<=n;i++){
    
    
            if(!st[i])primes[cnt++]=i;
            for(int j=0;primes[j]<=n/i;j++){
    
    
                st[primes[j]*i]=true;
                if(i%primes[j]==0)break;
            }
        }
    }
    static int get(int n,int p){
    
    
        int res=0;
        while(n>0){
    
    
            res+=n/p;
            n/=p;
        }
        return res;
    }
}

满足条件的01序列

在这里插入图片描述

题解

将 01 序列置于坐标系中,起点定于原点。若 0 表示向右走,11表示向上走,那么任何前缀中 0 的个数不少于 1 的个数就转化为,路径上的任意一点,横坐标大于等于纵坐标。题目所求即为这样的合法路径数量。
下图中,表示从 (0,0) 走到 (n,n) 的路径,在绿线及以下表示合法,若触碰红线即不合法。
在这里插入图片描述
由图可知,任何一条不合法的路径(如黑色路径),都对应一条从 (0,0) 走到 (n−1,n+1) 的一条路径(如灰色路径)。而任何一条 (0,0)走到 (n−1,n+1)的路径,也对应了一条从 (0,0)走到 (n,n)的不合法路径。
来源:https://www.acwing.com/solution/content/8907/
卡特兰数:
C 2 n n − C 2 n n − 1 = ( 2 n ) ! n ! n ! − ( 2 n ) ! ( n − 1 ) ! ( n + 1 ) ! = ( 2 n ) ! ( n + 1 ) − ( 2 n ) ! n ( n + 1 ) ! n ! = ( 2 n ) ! ( n + 1 ) ! n ! = 1 n + 1 ( 2 n ) ! n ! n ! = 1 n + 1 C 2 n n \begin{aligned} C_{2n}^n-C_{2n}^{n-1}&=\frac{(2n)!}{n!n!}-\frac{(2n)!}{(n-1)!(n+1)!} \\ &=\frac{(2n)!(n+1)-(2n)!n}{(n+1)!n!} \\ &=\frac{(2n)!}{(n+1)!n!} \\ &=\frac{1}{n+1} \frac{(2n)!}{n!n!} \\ &=\frac{1}{n+1} C_{2n}^n \\ \end{aligned} C2nnC2nn1=n!n!(2n)!(n1)!(n+1)!(2n)!=(n+1)!n!(2n)!(n+1)(2n)!n=(n+1)!n!(2n)!=n+11n!n!(2n)!=n+11C2nn

import java.io.*;

public class Main{
    
    
    static int mod=(int)(1e9+7);
    public static void main(String[] args)throws IOException{
    
    
        BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
        long n=Integer.parseInt(in.readLine());
        long res=1;
        for(long i=2*n,j=1;j<=n;j++,i--){
    
    
            res=res*i%mod;
            res=res*get(j,mod-2,mod)%mod;
        }
        System.out.println(res*get(n+1,mod-2,mod)%mod);
    }
    static long get(long a,long k,long p){
    
    
        long res=1;
        while(k>0){
    
    
            if((k&1)==1)res=res*a%p;
            k>>=1;
            a=a*a%p;
        }
        return res;
    }
}

猜你喜欢

转载自blog.csdn.net/m0_54136420/article/details/129740650
今日推荐