蓝桥杯Java ABC组 数论知识合集

Note Of Note

  1. 同余方程中是可以正常进行分数的约分和去分母的
  2. e x g c d exgcd exgcd 在使用时要保证 a , b a,b a,b 都是非负数
  3. a a a b b b 互质不代表 a , b a,b a,b 都为质数( 4 4 4 5 5 5 互质,但是 4 4 4 不是质数)
  4. 两个相邻的正整数必定是互质的
  5. 两个互质的数 a , b a,b a,b 凑不出来(特指用加法)的最大整数为 ( a − 1 ) ( b − 1 ) − 1 (a-1)(b-1)-1 (a1)(b1)1
  6. 一个大数的约数个数平均是 l o g n logn logn 级别,最坏是 n \sqrt{n} n 级别,int 范围内一个数的约数最多为 1600 个
  7. 1 到 n 的约数个数之和大概是在 n l o g n nlogn nlogn 这个级别
  8. 1 到 n 之间的质数大概有 n ln ⁡ n \dfrac{n}{\ln n} lnnn
  9. 只有完全平方数的因子个数为奇数个
  10. 任何一个合数 n 必定包含一个不超过 n \sqrt n n 的质因子

TODO

分解质因数,判断质数,分解因数时循环的范围与写法

质数

质数与合数是针对所有**大于 1 1 1 的自然数来定义的(所有小于等于 1 1 1 **的数都不是质数)。

判断质数

用试除法判断,时间复杂度为 O ( n ) O(\sqrt{n}) O(n )

public static boolean isPrime(int n){
        if(n < 2) return false;
        for(int i = 2;i <= n / i;i ++)
            if(n % i == 0) return false;
        return true;
}

也可以先把质数筛出来,然后用质数去试除,只需要以 O ( n ) O(n) O(n) 的时间复杂度预处理出 1 1 1 n \sqrt n n 里所有素数,每次判断的时间复杂度就可以降低到 O ( n ln ⁡ n ) O(\frac{\sqrt n}{\ln \sqrt n}) O(lnn n )

分解质因数

算术基本定理

任何一个大于 1 1 1 的自然数 n n n,如果本身不是质数,那么就可以唯一分解为有限个质数的乘积, N = p 1 a 1 × p 2 a 2 × p 3 a 3 × ⋯ × p n a n N=p_1^{a_1}\times p_2^{a_2}\times p_3^{a_3}\times \cdots \times p_n^{a_n} N=p1a1×p2a2×p3a3××pnan,这里 p 1 < p 2 < p 3 < ⋯ < p n p_1 < p_2 < p_3 < \cdots < p_n p1<p2<p3<<pn 且均为质数,其中指数 a i a_i ai 是正整数

分解质因数

最坏为 O ( n ) O(\sqrt{n}) O(n )

n n n 中最多只包含一个大于 n \sqrt{n} n 的质因子

public static void divide(int n){
        for(int i = 2;i <= n / i;i ++){
            if(n % i == 0){
                int s = 0;
                while(n % i == 0){
                    n /= i;
                    s ++;
                }
                printf("%d %d\n",i, s);
            }
        }
        if(n > 1) printf("%d %d\n",n, 1);
        printf("\n");
}

可以先预处理出 1 ∼ n 1\sim n 1n 中的素数,直接用素数去分解,单次分解的时间复杂度可以降到 O ( n ln ⁡ n ) O(\frac{\sqrt n}{\ln \sqrt n}) O(lnn n )

这个时间复杂度是比较暧昧的,大概能比 O ( n ) O(\sqrt n) O(n ) 低一个数量级, 1 0 6 10^6 106 的数据大概只需要 100 次计算即可。

筛质数

欧拉筛

时间复杂度为 O ( n ) O(n) O(n)

原理说明:

每个合数都只会被自己最小质因子筛掉,假设该最小质因子为 p j p_j pj,则合数 x = p j ∗ i x=p_j*i x=pji,因为 p j p_j pj从小到大依次枚举的,如果

  • i   m o d   p j ≠ 0 i\bmod p_j \ne 0 imodpj=0,说明 p j p_j pj 小于 i i i 的所有质因子,那么 p j p_j pj 一定是 p j ∗ i p_j*i pji 的最小质因子
  • i   m o d   p j = 0 i\bmod p_j = 0 imodpj=0,第一个这样的 p j p_j pj 也一定时 p j ∗ i pj*i pji 的最小质因子,而且可以停止枚举了,因为 p j + 1 p_{j+1} pj+1 已经大于了 i i i 的最小质因子了
int primes[N], cnt=0;     // primes[]存储所有素数
boolean st[N];         // st[x]存储x是否被筛掉

void getPrimes(int n){
    for (int i = 2; i <= n; i ++ ){
        if (!st[i]) primes[++cnt] = i;
        for (int j = 1; primes[j] <= n / i; j ++ ){
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

约数

试除法求约数

public static void getDivisors(int n){
        List<Integer> list = new ArrayList<>();
        for(int i=1;i<=n/i;i++){
            if(n%i==0){
                list.add(i);
                if(i!=n/i) list.add(n/i);
            }
        }
        list.sort((a,b)->{return a-b;});
        for(Integer a : list)
            cout.print(a+" ");
        cout.println();
}

求 n 的正约数集合像上面这么求的话,时间复杂度为 O ( n ) O(\sqrt n) O(n ),如果连续求 1 到 n 的正约数集合的话,就是 O ( n n ) O(n\sqrt n) O(nn )

我们可以用倍数法来求:
BCA42F8AC396C1E365ACF3E84172F197

直接枚举因数,然后把因数添加到相应的数的集合里,时间复杂度为 O ( n + n 2 + n 3 + n 4 + ⋯ + n n = O ( n log ⁡ n ) O(n+\frac{n}{2}+\frac{n}{3}+\frac{n}{4}+\cdots+\frac{n}{n}=O(n\log n) O(n+2n+3n+4n++nn=O(nlogn)

static List<Integer>[] factor;
public static void getFactors(int n){
	factor = new ArrayList[n+1];
	for(int i=1;i<=n;i++) factor[i] = new ArrayList<>();
	// 枚举因子
	for(int i=1;i<=n;i++)
		// 枚举倍数
		for(int j=1;j<=n/i;j++)
			factor[i*j].add(i);
}

约数相关公式

如果 N = p 1 a 1 × p 2 a 2 × p 3 a 3 × ⋯ × p n a n N=p_1^{a_1}\times p_2^{a_2}\times p_3^{a_3}\times \cdots \times p_n^{a_n} N=p1a1×p2a2×p3a3××pnan,那么:

  • N N N 的约数个数为 ( a 1 + 1 ) × ( a 2 + 1 ) × ⋯ × ( a n + 1 ) (a_1+1)\times(a_2+1)\times \cdots \times(a_n+1) (a1+1)×(a2+1)××(an+1)
  • N N N 的约数之和为 ( p 1 0 + p 1 1 + ⋯ + p 1 a 1 ) × ⋯ × ( p n 0 + p n 1 + ⋯ + p n a n ) (p_1^0+p_1^1+ \cdots + p_1^{a_1})\times \cdots \times (p_n^0+p_n^1+ \cdots + p_n^{a_n}) (p10+p11++p1a1)××(pn0+pn1++pnan)

以上的公式可以这么理解:

  • 约数个数相当于是乘法原理,每个指数都可以选一个值( 0 ∼ a i 0 \sim a_i 0ai,共 a i + 1 a_i+1 ai+1个数),只要有一个指数的值不同,数就不一样(算术基本定理),所以全部乘起来
  • 约数之和可以这样理解:指数的任意一种不同的组合,出来都是N的一个约数,原本是 p 1 0 ∗ p 2 0 + p 1 0 ∗ p 2 1 + p 1 1 ∗ p 2 0 + p 1 1 ∗ p 2 1 p_1^0*p_2^0+p_1^0*p_2^1+p_1^1*p_2^0+p_1^1*p_2^1 p10p20+p10p21+p11p20+p11p21,乘法分配律整理一下就行了

最大公因数

几个基本性质:

  1. d ∣ a , d ∣ b    ⟹    d ∣ a x + b y d\mid a,d \mid b\implies d\mid ax+by da,dbdax+by(读作“d能整除a,d能整除b,d就能整除ax+by”)
  2. a   m o d   b = a − ⌊ a b ⌋ ∗ b = a − c ∗ b a\bmod b=a- \left\lfloor\dfrac{a}{b}\right\rfloor *b=a-c*b amodb=abab=acb

所以 gcd ⁡ ( a , b ) = gcd ⁡ ( b , a − c ∗ b ) = gcd ⁡ ( b , a   m o d   b ) \gcd(a,b)=\gcd(b,a-c*b)=\gcd(b,a\bmod b) gcd(a,b)=gcd(b,acb)=gcd(b,amodb)

第一个等式两边的因数集合是一样的,左边的最大公因数就是右边的最大公因数( d d d 能整除 a a a d d d 能整除 b b b d d d 也就能整除 a − c ∗ b a-c*b acb

任何数都可以整除0,所以 b b b 0 0 0 时就返回 a a a

public static int gcd(int a,int b){
        if(b==0) return a;
        else return gcd(b,a%b);
}

两个数的积等于它们最大公约数和它们最小公倍数的积 a × b = gcd ⁡ ( a , b ) × lcm ⁡ ( a , b ) a\times b=\gcd(a,b)\times\operatorname{lcm}(a,b) a×b=gcd(a,b)×lcm(a,b)

欧拉函数

定义

ϕ ( n ) \phi(n) ϕ(n) 1 ∼ n 1\sim n 1n 中与 n n n 互质的数的个数

ϕ ( 6 ) = 2 \phi(6)=2 ϕ(6)=2

互质: a a a b b b 互质的充要条件是 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1 1 1 1 和任何数都互质

计算方法

n n n 为质数时, ϕ ( n ) = n − 1 \phi(n)=n-1 ϕ(n)=n1

n = p 1 a 1 × p 2 a 2 × p 3 a 3 × ⋯ × p n a n n=p_1^{a_1}\times p_2^{a_2}\times p_3^{a_3}\times \cdots \times p_n^{a_n} n=p1a1×p2a2×p3a3××pnan
ϕ ( n ) = n ∗ ∏ i = 1 x ( 1 − 1 p i ) \phi(n)=n*\prod\limits_{i=1}^x (1-\frac{1}{p_i}) ϕ(n)=ni=1x(1pi1)

简单证明

欧拉函数为积性函数,当 m , n m,n m,n 互质时,有 ϕ ( n m ) = ϕ ( n ) ∗ ϕ ( m ) \phi(nm)=\phi(n)*\phi(m) ϕ(nm)=ϕ(n)ϕ(m)

由唯一分解定理知, n = × p 2 a 2 × p 3 a 3 × ⋯ × p n a n n=\times p_2^{a_2}\times p_3^{a_3}\times \cdots \times p_n^{a_n} n=×p2a2×p3a3××pnan,因此 ϕ ( n ) = ϕ ( p 1 a 1 ) × ϕ ( p 2 a 2 ) ⋯ × ϕ ( p n a n ) \phi(n)=\phi(p_1^{a_1})\times \phi(p_2^{a_2})\cdots\times \phi(p_n^{a_n}) ϕ(n)=ϕ(p1a1)×ϕ(p2a2)×ϕ(pnan)

现考虑 ϕ ( p i a i ) \phi(p_i^{a_i}) ϕ(piai) 的求法,回归到定义,即为小于等于 p i a i p_i^{a_i} piai 的正整数中与其互质的数,从 1 1 1 p i a i p_i^{a_i} piai 一共有 p i a i p_i^{a_i} piai 个数字,其中与 p i a i p_i^{a_i} piai 不互质的有 p i , 2 p i , 3 p i , ⋯   , p i a i − 1 ∗ p i p_i,2p_i,3p_i,\cdots,p_i^{a_i-1}*p_i pi,2pi,3pi,,piai1pi,共 p i a i − 1 p_i^{a_i-1} piai1

所以 ϕ ( p i a i ) = p i a i − p i a i − 1 = p i a i ∗ ( 1 − 1 p i ) \phi(p_i^{a_i})=p_i^{a_i}-p_i^{a_i-1}=p_i^{a_i}*(1-\dfrac{1}{p_i}) ϕ(piai)=piaipiai1=piai(1pi1)

由此得证

求某个值的欧拉函数( O ( n ) O(\sqrt{n}) O(n )):

public static int phi(int a){
        int res = a;
        for(int i=2;i<=a/i;i++){
            if(a%i==0){
                res = res*(i-1)/i;
                while(a%i==0) a/=i;
            }
        }
        if(a>1) res = res*(a-1)/a;
        return res;
}

线性筛求欧拉函数( O ( n ) O(n) O(n)):

public static void getEuler(int n){
        phi[1]=1;
        for(int i=2;i<=n;i++){
            if(!ck[i]){
                primes[++cnt]=i;
                phi[i]=i-1;
            }
            for(int j=1;j<=cnt;j++){
                ck[i*primes[j]]=true;
                if(i%primes[j]==0){
                    phi[i*primes[j]] = phi[i]*primes[j];
                    break;
                }else 
                    phi[i*primes[j]] = phi[i]*(primes[j]-1);
            }
        }
}

模意义下的数和运算

定义: 对于整数 a , b a,b a,b,满足 b > 0 b>0 b>0,则存在唯一的整数 q , r q,r q,r,满足 a = b q + r a=bq+r a=bq+r,其中称 q q q 为商, r r r 为余数

说人话, a ÷ b a\div b a÷b 的商和余数是固定的

这个定义可以将同余式转换为不定方程

模意义下的运算法则

( a + b )   m o d   M = ( a   m o d   M + b   m o d   M )   m o d   M (a+b)\bmod M = (a\bmod M +b\bmod M)\bmod M (a+b)modM=(amodM+bmodM)modM

( a − b )   m o d   M = ( a   m o d   M − b   m o d   M )   m o d   M (a-b)\bmod M = (a\bmod M -b\bmod M)\bmod M (ab)modM=(amodMbmodM)modM

( a × b )   m o d   M = ( a   m o d   M × b   m o d   M )   m o d   M (a\times b)\bmod M = (a\bmod M \times b\bmod M)\bmod M (a×b)modM=(amodM×bmodM)modM
模意义下除法比较特殊

[!summary]
这几个运算法则用人话说就是:

  • 全部加完后再取模等于边加边模
  • 全部减完后再取模等于边减边模
  • 全部乘完后再取模等于边乘边模
  • 全部除完后再取模不等于边除边模

同余

定义:若两数 x , y x,y x,y 除以 b b b 的余数相等,则称 x , y x,y x,y b b b 同余,记做 x ≡ y ( m o d b ) x\equiv y \pmod{b} xy(modb)

重要性质:

  1. 同加性,同乘性,同幂性
  2. x ≡ y ( m o d b ) ⟺ b ∣ ( x − y ) x\equiv y \pmod{b} \Longleftrightarrow b\mid(x-y) xy(modb)b(xy) b b b 能整除 x − y x-y xy

欧拉定理与费马小定理

欧拉定理

a a a n n n 互质,则 a φ ( n ) ≡ 1 ( m o d n ) a^{\varphi (n)} \equiv 1 \pmod{n} aφ(n)1(modn)

费马小定理

p p p质数 a a a p p p 互质,则有 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 \pmod p ap11(modp)

快速幂

二进制思想

public static long qmi(long a,long k,long m){
        long res=1;
        while(k>0){
            if((k&1)==1) res=res*a%m;
            a=a*a%m;
            k>>=1;
        }
        return res;
}

扩展欧几里得算法

裴蜀定理

对于任意正整数 a , b a,b a,b,设他们的最大公约数 gcd ⁡ ( a , b ) = d \gcd(a,b)=d gcd(a,b)=d,则一定存在非零整数 x , y x,y x,y,使得 a x + b y = d ax+by=d ax+by=d

  • d d d a a a b b b 能凑出来的最小正整数
  • 对于整数 a , b , c a,b,c a,b,c a x + b y = c ax+by=c ax+by=c 有整数解当且仅当 gcd ⁡ ( a , b ) ∣ c \gcd(a,b) \mid c gcd(a,b)c

重要推论 a , b a,b a,b 互质的充要条件是存在整数 x , y x,y x,y 使 a x + b y = 1 ax+by=1 ax+by=1

说人话:两个数最小能”凑“出来的正数是他们俩的最大公倍数,并且所有能凑出来的数都是最大公倍数的倍数

这是两个数的情况,裴蜀定理也可以推广到 n n n 个数的情况

扩展欧几里得算法

public static int x,y;
public static int exgcd(int a, int b){
        if(b == 0){
            x = 1;y = 0;
            return a;
        }
        int d = exgcd(b, a % b);
        // 本层 ax1,by1   下一层 x0,y0
        // x1 = y0,y1 = x0-(a/b)*y0
        int temp = x;
        x = y;
        y = temp - a / b * y;
        return d;
}

此时得出的 x 0 , y 0 x_0,y_0 x0,y0 a x + b y = gcd ⁡ ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b) 的一组解,设 x , y x,y x,y 的最小公倍数为 gcd ⁡ ( a , b ) = d \gcd(a,b)=d gcd(a,b)=d,则有:
{ x = x 0 + b d ⋅ k y = y 0 − a d ⋅ k \begin{cases} x=x_0+\dfrac{b}{d}\cdot k\\y=y_0-\dfrac{a}{d}\cdot k\end{cases} x=x0+dbky=y0dak
注意这里的 x , y x,y x,y a x + b y = gcd ⁡ ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b) 的所有解,不是 a x + b y = c ax+by=c ax+by=c 的所有解

如何求 a x + b y = c ax+by=c ax+by=c 的所有解:

  • 先求出 a x + b y = gcd ⁡ ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b) 的一组特解 x 0 , y 0 x_0,y_0 x0,y0
  • 再把 x 0 , y 0 x_0,y_0 x0,y0 一起扩大 c gcd ⁡ ( a , b ) \dfrac{c}{\gcd(a,b)} gcd(a,b)c倍,得到 x 1 , y 1 x_1,y_1 x1,y1
  • 再计算 a x + b y = 0 ax+by=0 ax+by=0 的所有解,即 x = b gcd ⁡ ( a , b ) , y = − a gcd ⁡ ( a , b ) x=\dfrac{b}{\gcd(a,b)},y=-\dfrac{a}{\gcd(a,b)} x=gcd(a,b)b,y=gcd(a,b)a
  • 最后得到 a x + b y = c ax+by=c ax+by=c 的所有解

所以扩展欧几里得算法常用于求解线性同余方程,即给定 a , b , c a,b,c a,b,c,求方程 a x ≡ b ( m o d c ) ax \equiv b \pmod c axb(modc) 的解,等价于求 a x + c y = b ax+cy=b ax+cy=b 的解

逆元

定义:若 a × x ≡ 1 ( m o d p ) a\times x \equiv 1 \pmod p a×x1(modp),则称 x x x a a a 在模 p p p 意义下的乘法逆元,记为 a − 1 ( m o d p ) a^{-1} \pmod p a1(modp)

( a ÷ b )   m o d   M = ( a   m o d   M × b − 1 ( m o d M ) )   m o d   M (a \div b) \bmod M=(a\bmod M \times b^{-1}\pmod M)\bmod M (a÷b)modM=(amodM×b1(modM))modM

如果要分开算的话,才需要这么算,比如 4 ÷ 2 = 2 4\div2=2 4÷2=2,直接用 2 2 2 带入即可

定理: 在大于等于 0 0 0,小于 p p p 的范围内,模 p p p 的逆元(若存在)是唯一的

结论 a a a 在模 p p p 意义下的逆元存在当且仅当 a a a p p p 互质

计算方法

费马小定理

p p p质数 a a a p p p 互质,则 a a a 在模 p p p 意义下的乘法逆元为 a p − 2 a^{p-2} ap2

a p − 2 a^{p-2} ap2 可以用快速幂求解:qmi(a,p-2,p)

扩展欧几里得算法

a × x ≡ 1 ( m o d p ) a\times x \equiv 1 \pmod p a×x1(modp) a x + p y = 1 ax+py=1 ax+py=1 等价:

a × x ≡ 1 ( m o d p ) ⟺ p ∣ ( a x − 1 ) ⟺ ∃ y , 使得 ( a x − 1 ) = p y ⟺ ∃ y , 使得 a x − p y = 1 ⟺ ∃ y , 使得 a x + p y = 1 \begin{aligned}a\times x \equiv 1 \pmod p & \Longleftrightarrow p\mid(ax-1) \\ & \Longleftrightarrow \exists y,\text{使得} (ax-1)=py\\ & \Longleftrightarrow \exists y,\text{使得} ax-py=1 \\ & \Longleftrightarrow \exists y,\text{使得} ax+py=1\end{aligned} a×x1(modp)p(ax1)y,使得(ax1)=pyy,使得axpy=1y,使得ax+py=1

第一步转化是同余的重要性质,第二步转化是模的定义

这个式子是一个关于 a , p a,p a,p 的不定方程,还可以由裴蜀定理发现, a a a 在模 p p p 意义下的逆元当且仅当 gcd ⁡ ( a , p ) = 1 \gcd(a,p)=1 gcd(a,p)=1,即 a a a p p p 互质时才成立

public static int inv(int a,int p){
        exgcd(a, p);
        return x;
}

中国剩余定理

中国剩余定理可以求解下面这样一类同余方程组:
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 )   ⋮ x ≡ a n ( m o d m n ) \begin{cases}x\equiv a_1 \pmod {m_1} \\ x\equiv a_2 \pmod {m_2} \\ \, \quad\vdots \\ x\equiv a_n \pmod {m_n} \end{cases} xa1(modm1)xa2(modm2)xan(modmn)

其中, m 1 , m 2 , ⋯   , m n m_1,m_2,\cdots,m_n m1,m2,,mn 两两互质,则对于任意的整数 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an 上面这个方程组有解,解的构造方式为:

  • M = ∏ i = 1 n m i M=\prod_{i=1}^nm_i M=i=1nmi,设 M i = M m i M_i=\dfrac{M}{m_i} Mi=miM
  • t i = M i − 1 t_i=M_i^{-1} ti=Mi1 M i M_i Mi m i m_i mi 的逆元(易知 M i M_i Mi m i m_i mi 是互质的,所以逆元一定存在)
  • x x x 的通解为 x = k M + ∑ i = 1 n a i t i M i , k ∈ Z x=kM+\sum_{i=1}^na_it_iM_i,k\in Z x=kM+i=1naitiMi,kZ

组合数学

组合数

C a b C_a^b Cab 代表从 a a a 个物品中选出 b b b 个的方案数(不考虑顺序)

基本计算公式:

C a b = a ! b ! ( b − a ) ! C_a^b=\dfrac{a!}{b!(b-a)!} Cab=b!(ba)!a!

递推求组合数

时间复杂度为 O ( n 2 ) O(n^2) O(n2)

适用范围:

  • 1 ≤ n ≤ 10000 1\le n \le 10000 1n10000
  • 1 ≤ b ≤ a ≤ 2000 1 \le b \le a \le 2000 1ba2000
#include<iostream>
using namespace std;
const int mod = 1e9+7;
long long f[2010][2010];
int main()
{
    //预处理
    for(int i=0;i<=2000;i++)
    {
        for(int j=0;j<=i;j++)
        {
            if(j==0) f[i][j]=1;
            else f[i][j]=(f[i-1][j-1]+f[i-1][j])%mod;
        }
    }
    int n;
    cin>>n;
    while(n--)
    {
        int a,b;
        cin>>a>>b;
        printf("%ld\n",f[a][b]);
    }
}

预处理逆元求组合数

时间复杂度为 O ( a ∗ l o g ( m o d ) ) O(a*log(mod)) O(alog(mod))

适用范围:

  • 1 ≤ n ≤ 10000 1\le n \le 10000 1n10000
  • 1 ≤ b ≤ a ≤ 1 0 5 1 \le b \le a \le 10^5 1ba105

由基本公式得(在模的情况下进行)

C a b = a ! b ! ( b − a ) ! = a ! × b ! − 1 × ( b − a ) ! − 1 C_a^b=\dfrac{a!}{b!(b-a)!}=a!\times b!^{-1}\times (b-a)!^{-1} Cab=b!(ba)!a!=a!×b!1×(ba)!1

image-20230221095651140

static int n,m,k;
static long x,y,z;
static long[] fac,infac;
static final int INF = 0x3f3f3f3f, MOD = (int) 1e9+7;

public static void main(String[] args) throws IOException {
    int lim = (int) 1e5;
    fac = new long[lim+1];
    infac = new long[lim+1];
    fac[0]=infac[0]=1;
    for(int i=1;i<=lim;i++){
        fac[i]=i*fac[i-1]%MOD;
        infac[i]=infac[i-1]*qmi(i,MOD-2,MOD)%MOD;
    }

    n = nextInt();
    for(int i=1;i<=n;i++){
        int a =nextInt(),b = nextInt();
        long res = fac[a]*infac[b]%MOD*infac[a-b]%MOD;
        cout.println(res);
    }
    cout.flush();
}// End of main

public static long qmi(long a,long k,long p){
    long res = 1;
    while(k>0){
        if((k&1)==1) res = res*a%p;
        a = a*a%p;
        k>>=1;
    }
    return res;
}

Lucas定理求组合数

C a b ≡ C a p b p ⋅ C a   m o d   p b   m o d   p ( m o d p ) C_a^b\equiv C_{\frac{a}{p}}^{\frac{b}{p}} \cdot C_{a \bmod p}^{b \bmod p} \pmod p CabCpapbCamodpbmodp(modp)

适用范围:

  • 1 ≤ n ≤ 20 1\le n \le 20 1n20
  • 1 ≤ b ≤ a ≤ 1 0 18 1 \le b \le a \le 10^{18} 1ba1018
  • 1 ≤ p ≤ 1 0 5 1\le p \le 10^5 1p105

[!important]
long 类型的读入最好用 Scanner,要不然你咋挂的都不知道

static int n,m,k;
static long x,y,z;
static long[] fac,infac;
static final int INF = 0x3f3f3f3f, MOD = (int) 1e9+7;

public static void main(String[] args) throws IOException {
    Scanner cin = new Scanner(System.in);
    n = cin.nextInt();
    while(n-->0){
        long a = cin.nextLong(),b = cin.nextLong(),p=cin.nextLong();
        cout.println(lucas(a, b, p));
    } 
    cout.flush();
}// End of main

public static long lucas(long a,long b,long p){
    if(a<p && b<p) return C(a,b,p);
    // a%p后肯定是<p的,所以可以用C(),但a/p后不一定<p 所以用lucas继续递归
    return lucas(a/p, b/p, p)*C(a%p,b%p,p)%p;
}

public static long C(long a,long b,long p){
	long res = 1;
	// C_a^b 的公式求法就是
	// a!/(b!(a-b)!) = (a-b+1)*...*a / b! 分子有b项
	// i -> 1 - b    j -> a - a-b+1 
	for(long i=1,j=a;i<=b;i++,j--){
		res = res*j%p;
		res = res*qmi(i,p-2,p)%p;
	}
	return res;
}

public static long qmi(long a,long k,long p){
    long res = 1;
    while(k>0){
        if((k&1)==1) res = res*a%p;
        a = a*a%p;
        k>>=1;
    }
    return res;
}

卡特兰数

6828_9476d97655-Catalan

卡特兰数计算公式:

C 2 n n − C 2 n n − 1 = C 2 n n n − 1 C_{2n}^n-C_{2n}^{n-1}=\dfrac{C_{2n}^n}{n-1} C2nnC2nn1=n1C2nn
image-20230221144014759

f ( n ) = f ( 0 ) f ( n − 1 ) + f ( 1 ) f ( n − 2 ) + + f ( n − 1 ) f ( 0 ) f(n) = f(0)f(n-1) + f(1)f(n-2) + + f(n-1)f(0) f(n)=f(0)f(n1)+f(1)f(n2)++f(n1)f(0)

1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862,

应用:

  • 出栈次序
  • n对括号正确匹配数目
  • 给定节点组成二叉搜索树
  • 在圆上选择 2 n 2n 2n 个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数
  • 求一个凸多边形区域划分成三角形区域的方法数

期望

在OI中,期望一般指的就是达到结果的期望,最朴素的计算是每次可能结果的概率乘以其结果的总和

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值