数论基础知识点整理(基础篇)

这是我在ACM竞赛中学习数论时整理的一些基础的知识点,主要讨论整除、同余、最大公约数以及素数及其判定、快速幂、逆元,写的不太专业,有错误的地方还请多批评指正!

文章目录

  • 整除
  • 同余
  • 最大公约数
    • 扩展欧几里德例题
  • 最小公倍数
  • 素数及其判定
  • 整除分块
  • 快速幂
  • 逆元
    • O(n)计算阶乘的逆元

整除

  • 定义: 整数 a , b ∈ Z , 且 a ≠ 0 a, b\in\Z,且a\neq0 a,bZ,a=0,若 ∃ q \exists q q,使得 b = a q b=aq b=aq,则称b可被a整除(或a能整除b),记作 a ∣ b a\mid b ab;且称b是a的倍数,a是b的约数(或因子、因数)。若a不能整除b,则记作 a ∤ b a\nmid b ab
  • 定理
    • 1. a ∣ b , 且 b ∣ c , 则 a ∣ c a\mid b,且b\mid c, 则a\mid c abbcac
      • 证明: ∵ a ∣ b , 且 b ∣ c ; 令 b = p a , c = q b , ∴ c = p q a \because a\mid b,且b\mid c;令b=pa,c=qb,\therefore c=pqa ab,bc;b=pa,c=qbc=pqa,根据整除定义即 a ∣ c a\mid c ac
    • 2. a ∣ b , 且 a ∣ c , 则 ∀ x , y , 满 足 a ∣ b x + c y a\mid b,且a\mid c,则\forall x,y,满足a\mid bx+cy ab,ac,x,y,abx+cy
      • 证明: ∵ a ∣ b 且 a ∣ c , 令 b = p a , c = q a , 则 b x + c y = p a x + q a y = ( p x + q y ) a , ∴ a ∣ b x + c y \because a\mid b且a\mid c,令b=pa,c=qa,则bx+cy=pax+qay=(px+qy)a,\therefore a\mid bx+cy abac,b=pa,c=qa,bx+cy=pax+qay=(px+qy)a,abx+cy
    • 3. a ≠ 0 , b = a q + c a\neq0,b=aq+c a=0,b=aq+c,那么 a ∣ b a\mid b ab 的充分必要条件是 a ∣ c a\mid c ac
      • 充分性证明: ∵ a ∣ b , 令 b = p a , 则 c = ( p − q ) a , ∴ a ∣ c \because a\mid b,令b=pa,则c=(p-q)a,\therefore a\mid c ab,b=pa,c=(pq)a,ac
      • 必要性证明: ∵ a ∣ c , 令 c = p a , 则 b = ( p + q ) a , ∴ a ∣ b \because a\mid c,令c=pa,则b=(p+q)a,\therefore a\mid b ac,c=pa,b=(p+q)a,ab

同余

∀ a , b ∈ Z , 且 a ≠ 0 , ∃ p , c \forall a,b\in\Z,且a\neq 0,\exists p,c a,bZ,a=0,p,c,使得等式 b = p a + c , 0 ⩽ c ⩽ a − 1 b=pa+c,0\leqslant c\leqslant a-1 b=pa+c,0ca1成立

  • 取模运算
    b对a取模的结果即b除以a的余数,符号表示为 b m o d    a b\mod a bmoda,计算值为 b − ⌊ b a ⌋ ∗ a b-\lfloor \frac{b}{a}\rfloor*a baba
  • 同余
    m ⩾ 1 , a , b ∈ Z , 若 m ∣ ( a − b ) m\geqslant 1,a,b\in\Z,若m\mid(a-b) m1,a,bZ,m(ab),则称a与b关于模m同余,符号表示为 a ≡ b ( m o d   m ) a\equiv b(mod\ m) ab(mod m)
  • 性质
    • 1. 自反性: a ≡ a ( m o d   m ) a\equiv a(mod\ m) aa(mod m)
    • 2. 对称性: a ≡ b ( m o d   m ) 则 b ≡ a ( m o d   m ) a\equiv b(mod\ m)则b\equiv a(mod\ m) ab(mod m)ba(mod m)
    • 3. 传递性: a ≡ b ( m o d   m ) 且 b ≡ c ( m o d   m ) 则 a ≡ c ( m o d   m ) a\equiv b(mod\ m)且b\equiv c(mod\ m)则a\equiv c(mod\ m) ab(mod m)bc(mod m)ac(mod m)
      • 证明: ∵ a ≡ b ( m o d   m ) 且 b ≡ c ( m o d   m ) \because a\equiv b(mod\ m)且b\equiv c(mod\ m) ab(mod m)bc(mod m)则根据同余定义 m ∣ ( a − b ) , m ∣ ( b − c ) m\mid (a-b),m\mid(b-c) m(ab),m(bc),再根据整除的定理2, m ∣ [ ( a − b ) + ( b − c ) ] , 即 m ∣ ( a − c ) m\mid[(a-b)+(b-c)],即m\mid(a-c) m[(ab)+(bc)],m(ac),再由同余定义得 a ≡ c ( m o d   m ) a\equiv c(mod\ m) ac(mod m)
    • 4. 同余式相加: a ≡ b ( m o d   m ) 且 c ≡ d ( m o d   m ) 则 a ± c ≡ b ± d ( m o d   m ) a\equiv b(mod\ m)且c\equiv d(mod\ m)则a\pm c\equiv b\pm d(mod\ m) ab(mod m)cd(mod m)a±cb±d(mod m)
      • 证明: ∵ a ≡ b ( m o d   m ) 且 c ≡ d ( m o d   m ) ∴ m ∣ ( a − b ) , m ∣ ( c − d ) ∴ m ∣ [ ( a − b ) + ( c − d ) ] , m ∣ [ ( a + c ) − ( b + d ) ] ∴ a + c ≡ b + d ( m o d   m ) \because a\equiv b(mod\ m)且c\equiv d(mod\ m)\therefore m\mid(a-b),m\mid(c-d)\therefore m\mid[(a-b)+(c-d)],m\mid[(a+c)-(b+d)]\therefore a+c\equiv b+d(mod\ m) ab(mod m)cd(mod m)m(ab),m(cd)m[(ab)+(cd)],m[(a+c)(b+d)]a+cb+d(mod m)同理也能证明 a − c ≡ b − d ( m o d   m ) a-c\equiv b- d(mod\ m) acbd(mod m)
    • 5. 同余式相乘: a ≡ b ( m o d   m ) 且 c ≡ d ( m o d   m ) 则 a c ≡ b d ( m o d   m ) a\equiv b(mod\ m)且c\equiv d(mod\ m)则ac\equiv bd(mod\ m) ab(mod m)cd(mod m)acbd(mod m)
      • 证明: ∵ a ≡ b ( m o d   m ) 且 c ≡ d ( m o d   m ) ∴ m ∣ ( a − b ) , m ∣ ( c − d ) ∴ m ∣ [ ( a − b ) c + ( c − d ) b ] ∴ m ∣ ( a c − b d ) ∴ a c ≡ b d ( m o d   m ) \because a\equiv b(mod\ m)且c\equiv d(mod\ m)\therefore m\mid(a-b),m\mid(c-d)\therefore m\mid[(a-b)c+(c-d)b]\therefore m\mid(ac-bd) \therefore ac\equiv bd(mod\ m) ab(mod m)cd(mod m)m(ab),m(cd)m[(ab)c+(cd)b]m(acbd)acbd(mod m)
    • 6. 除法: a c ≡ b c ( m o d   m ) 则 a ≡ b ( m o d   m g c d ( c , m ) ) ac\equiv bc(mod\ m)则a\equiv b(mod \ \frac{m}{gcd(c, m)}) acbc(mod m)ab(mod gcd(c,m)m) g c d ( c , m ) gcd(c, m) gcd(c,m)就是c和m的最大公约数
      • 证明: d = g c d ( c , m ) d=gcd(c,m) d=gcd(c,m),则令 c = p d , m = q d c=pd,m=qd c=pd,m=qd,满足 g c d ( p , q ) = 1 gcd(p,q)=1 gcd(p,q)=1 ∵ a c ≡ b c ( m o d   m ) ⇒ q d ∣ ( a − b ) p d ⇒ q ∣ ( a − b ) p ∵ g c d ( p , q ) = 1 ∴ q ∣ ( a − b ) ∴ a ≡ b ( m o d   q ) \because ac\equiv bc(mod\ m)\Rightarrow qd\mid(a-b)pd\Rightarrow q\mid(a-b)p\because gcd(p, q)=1\therefore q\mid(a-b)\therefore a\equiv b(mod\ q) acbc(mod m)qd(ab)pdq(ab)pgcd(p,q)=1q(ab)ab(mod q),q即 m g c d ( c , m ) \frac{m}{gcd(c, m)} gcd(c,m)m
    • 7. 幂运算: a ≡ b ( m o d   m ) 则 a n ≡ b n ( m o d   m ) a\equiv b(mod\ m)则a^{n}\equiv b^{n}(mod\ m) ab(mod m)anbn(mod m)

最大公约数

最大公约数(Greatest Common Divisor,简称GCD

  • 定义: a,b的公共的约数中最大的约数,记作 g c d ( a , b ) gcd(a,b) gcd(a,b) g c d ( a , b ) = m a x { d , d ∣ a 且 d ∣ b } gcd(a,b)=max \{ d, d\mid a且d\mid b\} gcd(a,b)=max{ d,dadb}

  • 性质:

    • 1. g c d ( a , b ) = g c d ( b , a ) gcd(a,b)=gcd(b,a) gcd(a,b)=gcd(b,a)
    • 2. g c d ( a , b ) = g c d ( a , a ± b ) gcd(a,b)=gcd(a,a\pm b) gcd(a,b)=gcd(a,a±b)
      • 证明:请点击链接
    • 3. g c d ( a , b ) = g c d ( a ± m b ) gcd(a,b)=gcd(a\pm mb) gcd(a,b)=gcd(a±mb)
    • 4. g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)
      • 证明: g c d ( b , a % b ) = g c d ( b , a − ⌊ a b ⌋ ∗ b ) gcd(b,a\%b)=gcd(b,a-\lfloor\frac{a}{b}\rfloor*b) gcd(b,a%b)=gcd(b,abab),根据性质1、3可得gcd(a,b)=gcd(b,a%b)
    • 5. g c d ( m a , m b ) = m ∗ g c d ( a , b ) gcd(ma,mb)=m*gcd(a,b) gcd(ma,mb)=mgcd(a,b)
      • 证明: d = g c d ( a , b ) , 则 d ∣ a , d ∣ b , 满 足 a = p d , b = q d 且 g c d ( p , q ) = 1 ∴ g c d ( m a , m b ) = g c d ( m d p , m d q ) = m d d=gcd(a,b),则d|a,d|b,满足a=pd,b=qd且gcd(p,q)=1\therefore gcd(ma,mb)=gcd(mdp,mdq)=md d=gcd(a,b),da,db,a=pd,b=qdgcd(p,q)=1gcd(ma,mb)=gcd(mdp,mdq)=md
  • 求解方法
    辗转相除法(欧几里德法)

    • 原理:
      g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)
      当b=0时,gcd(a,b)=a,否则根据公式继续迭代。
    • 代码实现:
       int gcd(int a, int b){
               
      	return b == 0 ? a : gcd(b, a%b);
      }
      
  • 扩展欧几里德:ax+by=gcd(a,b)

    • 原理:
      令 d = g c d ( a , b ) 令d=gcd(a,b) d=gcd(a,b)
      ∵ a x + b y = g c d ( a , b ) \because ax+by=gcd(a,b) ax+by=gcd(a,b)
      ∴ b x ′ + ( a % b ) y ′ = g c d ( b , a % b ) \therefore bx^{'} +(a\%b)y^{'}=gcd(b,a\%b) bx+(a%b)y=gcd(b,a%b)
      ∴ b x ′ + ( a % b ) y ′ = a x + b y \therefore bx^{'}+(a\%b)y^{'}=ax+by bx+(a%b)y=ax+by
      ∴ a y ′ + b ( x ′ − ⌊ a b ⌋ ∗ y ′ ) = a x + b y \therefore ay^{'}+b(x^{'}-\lfloor\frac{a}{b}\rfloor*y^{'})=ax+by ay+b(xbay)=ax+by要使任意a,b都满足此等式,
      x = y ′ , y = x ′ − ⌊ a b ⌋ ∗ y ′ x=y^{'},y=x^{'}-\lfloor\frac{a}{b}\rfloor*y^{'} x=y,y=xbay
      当b=0时,ax=a,则x=1,y=0;
      否则,令用 g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)递归求解,回溯时利用两式之间的关系,更新x,y,可求得方程的一组整数解。
    • 代码实现:
      int exgcd(int a, int b, int &x, int &y){
               
      	if(!b) {
               x = 1; y = 0; return a;}
      	int d = exgcd(b, a % b, x, y);
      	int z = x; x = y; y = z - (a / b) * y;
      	return d;
      }
      
    • 方程 a x + b x = c ax+bx=c ax+bx=c
      利用扩展欧几里德算法可求得 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)的一组解 x 0 , y 0 x_0,y_0 x0,y0;令d=gcd(a,b)
      + 当 d ∣ c d\mid c dc时,方程有解,其一组解为:
      x = x 0 ∗ c d , y = y 0 ∗ c d x=x_0*\frac{c}{d},y=y_0*\frac{c}{d} x=x0dc,y=y0dc
      通解为:
      x = x 0 ∗ c d + k ∗ b d x=x_0*\frac{c}{d}+k*\frac{b}{d} x=x0dc+kdb
      y = y 0 ∗ c d + k ∗ a d , ( k ∈ Z ) y=y_0*\frac{c}{d}+k*\frac{a}{d},(k\in\Z) y=y0dc+kda,(kZ)

扩展欧几里德例题

  • POJ1061
  • 题意: 两只青蛙在同一条维度线上朝西跳动, 青蛙A起始点坐标x,每次跳动m米,青蛙B起始点坐标y,每次跳动n米,问他们跳动多少次能沟相遇,不能输出Impossible
  • 题解: 设两只青蛙跳动次数为t,则此时青蛙A的坐标为(x+mt)%l,青蛙B的坐标为(y+nt)%l,当两只青蛙相遇时,满足
    ( x + m t ) ≡ ( y + n t ) ( m o d    l ) (x+mt)\equiv(y+nt)(\mod l) (x+mt)(y+nt)(modl)
    l ∣ ( ( x + m t ) − ( y + n t ) ) l\mid((x+mt)-(y+nt)) l((x+mt)(y+nt))
    ∃ k ∈ Z \exists k\in\Z kZ使得等式 ( ( x + m t ) − ( y + n t ) ) = k l ((x+mt)-(y+nt))=kl ((x+mt)(y+nt))=kl成立
    ( m − n ) t + l k = x − y (m-n)t+lk=x-y (mn)t+lk=xy
    g c d ( m − n , l ) ∣ ( x − y ) gcd(m-n,l)\mid(x-y) gcd(mn,l)(xy)时,方程有解,即答案就是t的最小正整数解,否则方程无解,则两青蛙永远不能相遇。
  • 代码实现:
    #include 
    #include 
    #include 
    #include 
    using namespace std;
    #define mes(a, val) memset(a, val, sizeof a)
    #define mec(b, a) memcpy(b, a, sizeof a)
    #define ll long long
    
    ll exgcd(ll a, ll b, ll &x, ll &y){
           
    	if(!b) {
           x = 1; y = 0; return a;}
    	ll d = exgcd(b, a % b, x, y);
    	ll z = x; x = y; y = z - (a / b) * y;
    	return d;
    }
    int main()
    {
           
        ll n, m, x, y, l;
        scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &l);
        ll a = n - m;
        ll b = l;
        ll c = x - y;
        ll d = exgcd(a, b, x, y);
        if(d < 0) d = -d;
        if(c % d){
           
            printf("Impossible\n");
        }
        else {
           
            ll p = b / d;
            x = x * c / d;
            x = ((x % p) + p) % p;
            printf("%lld\n", x);
        }
        return 0;
    }
    
    

最小公倍数

最小公倍数(Least Common Multiple 简称LCM

  • 定义: a, b的所有公共的倍数中最小的公倍数,记作lcm(a,b), l c m ( a , b ) = m i n { d , a ∣ d 且 b ∣ d } lcm(a,b)=min\{d,a\mid d且b\mid d\} lcm(a,b)=min{ d,adbd}
  • 计算方法: 公式 l c m ( a , b ) = a ∗ b g c d ( a , b ) lcm(a,b)=\frac{a*b}{gcd(a,b)} lcm(a,b)=gcd(a,b)ab

素数及其判定

  • 素数(也称质数): 一个正整数n是素数当且仅当只能被1和其自身整除,否则就称n是合数
    注: 1既不是素数也不是合数,最小的素数是2
  • 素数判定
    • 1. 暴力枚举:
      • 原理: 循环遍历i从2到 n \sqrt{n} n ,只要存在 n % i = 0 n\%i=0 n%i=0即可判定n不是素数,这样每次判定时间复杂度都是 O ( n ) O(\sqrt{n}) O(n )
      • 代码实现
        bool isprime(int n){
                   
        	for(int i = 2; i * i <= n; i ++){
                   
        		if(n % i == 0) return false;		
        	}
        	return true;
        }
        
    • 2. 埃拉托斯特尼筛法,简称埃氏筛:
      是一种用来求自然数n以内的全部素数的方法 。
      • 原理: 一个合数必然可以表示一个质数和另一个数相乘。对于一个素数p,那么p的倍数2p,3p,…kp,…必然都是合数。时间复杂度为 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
      • 实现代码:
        /*
        	定义数组:isprime[i],表示数组i是否时素数
        	
        */
        const int maxn = 1e5 + 100;
        int m;
        bool isprime[maxn];
        int p[maxn];
        void sieze(int n)
        {
                   
            m = 0; memset(isprime, true, sizeof(isprime));
            for(int i = 2; i <= n; i ++){
                   
                if(isprime[i]){
                   ///表明 i 就是素数
                	p[++ m] = i;
                    for(int j = 2 * i; j <= n; j += i){
                   
                        isprime[j] = false;
                    }
                }
            }
        }
        
      • 不足: 仔细分析可以看出,这种方法筛出n以内的所有素数还是有不足之处的。原因在于每个合数会被其每一个质因子筛到一次。因此就有了接下来的线性筛。
    • 3. 线性筛
      它能在 O ( n ) O(n) O(n)的时间复杂度内筛选出n以内的所有素数。
      • 原理:
        线性筛能保证n以内的任何一个数只能被他的最小质因子筛一次。
        一个数n可以表示成 n = F a c t o r y m a x ∗ p n=Factory_{max}*p n=Factorymaxp
        F a c t o r y m a x Factory_{max} Factorymax是除n以外的最大因子,p是n的质因子,满足p小于等于 F a c t o r y m a x Factory_{max} Factorymax 的所有质因子。
        用数组v[i]表示i是否是素数,v[i]为false时表示i是素数,
        然后便利之前筛出的所有素数,v[i*p[j]]则不是素数,标记为true。 i % p[j] 为0时,跳出第二层循环,以此保证每个数只被他的最小质因子筛一次。
        当i %p[j]为0时,p[j]已经是i的最小值质因子,且p[j]时i*p[j]的最小值质因子,p[j+1]大于i的最小质因子,也就不是i*p[j]的最小质因子。
      • 代码实现:
      const int maxn = 1e5 + 100;
      int m;
      bool v[maxn];
      int p[maxn];
      void sieze(int n)
      {
               
          m = 0; memset(v, false, sizeof v);
          for(int i = 2; i <= n; i ++){
               
              if(!v[i]) {
               
                  p[++ m] = i;
              }
              for(int j = 1; j <= m && i * p[j] <= n; j ++){
               
                  v[i * p[j]] = true;
                  if(i % p[j] == 0) break;//这条语句很关键
              }
          }
      }
      

整除分块

计算表达式: ∑ i = 1 n ⌊ n i ⌋ \sum_{i=1}^{n}\lfloor\frac{n}{i}\rfloor i=1nin

  • 1.暴力O(n)求解不多赘述。
  • 2. O n O\sqrt{n} On 优美解法
    • 证明请看,这位大佬的证明非常清晰,很容易理解。
    • 代码实现
    int solve(int n)
    {
           
        int ans = 0;
        for(int l = 1, r; l <= n; l = r + 1){
           
            r = n / (n / l);
            ans += (r - l + 1) * (n / l);
        }
        return ans;
    }
    
  • 例题Gym100923I
    • **题意:**有n个男的,n个女的,第i个人都有为当前一个大小为i的懒惰值,当一男一女懒惰值的乘积<=n他们就就可以一起跳舞,请问有多少种组合可能;
    • 题解: 从1~n枚举男生的懒惰值i,满足条件的女女生个数就是 ⌊ n i ⌋ \lfloor\frac{n}{i}\rfloor in a n s = ∑ i = 1 n ⌊ n i ⌋ ans=\sum_{i=1}^{n}\lfloor\frac{n}{i}\rfloor ans=i=1nin,经典的整除分块例题。
    • 代码实现:
    #include 
    using namespace std;
    #define mes(a, val) memset(a, val, sizeof a)
    #define mec(b, a) memcpy(b, a, sizeof a)
    #define ll long long
    
    ll solve(ll n)
    {
           
        ll ans = 0;
        for(ll l = 1, r; l <= n; l = r + 1){
           
            r = n / (n / l);
            ans += (r - l + 1) * (n / l);
        }
        return ans;
    }
    int main()
    {
            
        freopen("perechi3.in", "r", stdin);
        freopen("perechi3.out", "w", stdout);
        int T; scanf("%d", &T);
        while(T --){
           
            ll n; scanf("%lld", &n);
            ll ans = solve(n);
            printf("%lld\n", ans);
        }
        return 0;
    }
    

快速幂

51Nod快速幂

  • 问题描述: 计算 a n a^{n} an为例
  • 问题分析: 显然 O ( n ) O(n) O(n)暴力是不能解决问题的,下面我们介绍一种算法 (快速幂) ,它能在 O ( l o g n ) O(log n) O(logn)时间复杂度内解决此问题。
  • 快速幂原理:
    将正整数n二进制拆分成 n = b n − 1 b n − 2 . . . b 0 , b i ∈ { 0 , 1 } n=b_{n-1}b_{n-2}...b_{0},b_{i}\in\{0,1\} n=bn1bn2...b0bi{ 0,1}
    n = 2 n − 1 b n − 1 + 2 n − 2 b n − 2 + . . . + 2 0 b 0 n=2^{n-1}b_{n-1}+2^{n-2}b_{n-2}+...+2^{0}b_{0} n=2n1bn1+2n2bn2+...+20b0
    ∴ a n = a 2 n − 1 b n − 1 + 2 n − 2 b n − 2 + . . . + 2 0 b 0 \therefore a^{n}=a^{2^{n-1}b_{n-1}+2^{n-2}b_{n-2}+...+2^{0}b_{0}} an=a2n1bn1+2n2bn2+...+20b0
    = a 2 n − 1 b n − 1 ∗ a 2 n − 2 b n − 2 ∗ . . . ∗ a 2 0 b 0 \quad\quad=a^{2^{n-1}b_{n-1}}*a^{2^{n-2}b_{n-2}}*...*a^{2^{0}b_{0}} =a2n1bn1a2n2bn2...a20b0
    因此我们可以设置一个res(初始为1)和一个base(初始值为a),也即a^{0},在对n进行二进制拆分过程中,当第i位(从右往左第i位)为1时,则将res乘上base,并且base每次都乘上自己。其实在整个过程中, b a s e i = a 2 i base_{i}=a^{2^{i}} basei=a2i。时间复杂度 O ( l o g ( n ) ) O(log(n)) O(log(n))
    int ksm(int a, int n, int mod)
    {
           
    	int res = 1;
    	while(n){
           
    		if(n & 1) res = res * a % mod; //二进制拆分n第i位(从右往左)为1
    		a = a * a % mod;
    		n >>= 1;
    	}
    	 return res;
    }
    

逆元

例题hdu1576

  • 定义: 整数a,b,若 a ∗ b ≡ 1 ( m o d    p ) a*b\equiv1(\mod p) ab1(modp),那么则称a和b互为模p意义下的逆元。
    • 注:逆元需要在取模下才有意义,a对模p意义下的逆元即a在模p意义下的倒数
  • 逆元的意义: 如何求解 a b % p \frac{a}{b}\% p ba%p(p是质数)?
    整数的除法运算是向下取整,若b不能整除a,进行除法运算后在对p取模,多得到的并是正确结果,这一点应该很好理解。
    逆元的作用就在如此,他能将除以一个数等价于乘上这个的逆元。
    a b ≡ a ∗ 1 b ( m o d    p ) \frac{a}{b}\equiv a*\frac{1}{b}(\mod p) baab1(modp)
    设b在模p意义下的逆元为invb,则
    i n v b ∗ b ≡ 1 ( m o d    p ) ⇔ i n v b ≡ 1 b ( m o d    p ) invb*b\equiv1(\mod p)\Leftrightarrow invb\equiv\frac{1}{b}(\mod p) invbb1(modp)invbb1(modp)
    ∴ a b ≡ a ∗ i n v b ( m o d    p ) \therefore \frac{a}{b}\equiv a*invb(\mod p) baainvb(modp)
  • 逆元求解方法
    • 1.费马小定理
      • 满足条件: 模数p必须是素数。
      • 原理: a p − 1 ≡ 1 ( m o d    p ) a^{p-1}\equiv1(\mod p) ap11(modp),p是素数 (再此不做证明)
        因此 a ∗ a p − 2 ≡ 1 ( m o d    p ) a*a^{p-2}\equiv1(\mod p) aap21(modp)
        a p − 2 a^{p-2} ap2就是a在模p意义下的逆元, i n v a = a p − 2 inva=a^{p-2} inva=ap2,利用快速幂可以求解
    • 2.扩展欧几里德
      • 满足条件: g c d ( a , p ) = 1 gcd(a,p)=1 gcd(a,p)=1即a与p互质。
      • 原理: i n v a ∗ a ≡ 1 ( m o d    p ) inva *a\equiv1(\mod p) invaa1(modp)
        ∴ p ∣ ( i n v a ∗ a − 1 ) \therefore p\mid(inva*a-1) p(invaa1)
        ∴ ∃ k ∈ Z , i n v a ∗ a − 1 = p k \therefore \exists k\in\Z,inva*a-1=pk kZ,invaa1=pk
        ∴ i n v a ∗ a + k ∗ p = 1 , ∵ g c d ( a , p ) = 1 \therefore inva*a+k*p=1,\because gcd(a,p)=1 invaa+kp=1,gcd(a,p)=1
        因此次方程一定有解,利用扩展欧几里德算法可求得inva
    • 递推公式
      • 满足条件: 模数p必定是素数 。
      • 原理:
        p = p % a + ⌊ p a ⌋ ∗ a p=p\%a+\lfloor\frac{p}{a}\rfloor*a p=p%a+apa
        x = p % a , y = ⌊ p a ⌋ x=p\%a,y=\lfloor\frac{p}{a}\rfloor x=p%a,y=ap
        p % p = ( x + y ∗ a ) % p p\%p=(x+y*a)\%p p%p=(x+ya)%p
        a − 1 % p = ( p − x ) ∗ y − 1 % p a^{-1}\%p=(p-x)*y^{-1}\%p a1%p=(px)y1%p
        因此得 a − 1 = ( p − ⌊ p a ⌋ ) ∗ ( p % a ) − 1 % p a^{-1}=(p- \lfloor\frac{p}{a}\rfloor)*(p\% a)^{-1}\%p a1=(pap)(p%a)1%p
        由此递推式可O(n)求解1~n所有数模p的逆元。
      • 代码实现:
      int inv[mod+5];
      void getInv(int  p)
      {
               
      	inv[1] = 1;
      	for(int i = 2; i < p; i ++){
               
      		inv[i] = (p - p / i) * inv[p % i] % p;
      	}
      }
      

O(n)计算阶乘的逆元

  • 原理:
    f ( n ) = n ! f(n)=n! f(n)=n!
    f ( n ) = f ( n − 1 ) ∗ n f(n)=f(n-1)*n f(n)=f(n1)n
    f ( n − 1 ) − 1 = f ( n ) − 1 ∗ n f(n-1)^{-1}=f(n)^{-1}*n f(n1)1=f(n)1n
    O ( n ) O(n) O(n)预处理出1n的阶乘,计算出$f(n)^{-1}$则可通过递推公式往下求解1n-1以内所有阶乘的逆元。
  • 代码实现:
    const int maxn = 1e5 + 10;
    int f[maxn], invf[maxn];
    int p;
    int ksm(int a, int n, int p)
    {
           
        int res = 1;
        while(n){
           
            if(n & 1) res = 1ll * res * a % p;
            a = 1ll * a * a % p;
            n >>= 1;
        }
        return res;
    }
    void init(int n)
    {
           
        f[0] = 1; f[1] = 1;
        for(int i = 2; i <= n; i ++){
           
            f[i] = 1ll * f[i-1] * i % p;
        }
        invf[n] = ksm(f[n], p - 2, p);
        for(int i = n - 1; i >= 0; i --){
           
            invf[i] = 1ll * invf[i+1] * (i + 1) % p;
        }
    }
    
  • 例题
    51Nod1118
    • 题意:
      M * N的方格,一个机器人从左上走到右下,只能向右或向下走。有多少种不同的走法?由于方法数量可能很大,只需要输出Mod 10^9 + 7的结果。
    • 题解: 答案就是 C n + m n C_{n+m}^{n} Cn+mn
    • 代码实现:
    import java.math.*;
    import java.util.*;
    
    public class Main {
           
    	
    	public static long mod = 1000000007;
    	public static long f[] = new long[2100];
    	public static long invf[] = new long[2100];
    	
    	public static long ksm(long a, long n, long p) {
           
    		long res = 1;
    		while(n > 0) {
           
    			if(n % 2 == 1) res = res * a % p;
    			a = a * a % p;
    			n >>= 1;
    		}
    		return res;
    	}
    	//计算阶乘的逆元
    	public static void init(int n) {
           
    		f[0] = 1;
    		for(int i = 1; i <= n; i ++) {
           
    			f[i] = f[i-1] * i % mod;
    		}
    		invf[n] = ksm(f[n], mod-2, mod);
    		for(int i = n - 1; i >= 0; i --) {
           
    			invf[i] = invf[i+1] * (i + 1) % mod;
    		}
    	}
    	public static long get(int n, int m) {
           
    		n --; m --;
    		long res = f[n+m]*invf[n]%mod*invf[m]%mod;
    		res = res % mod;
    		return res;
    	}
    	public static void main(String[] args) {
           
    		Scanner cin = new Scanner(System.in);
    		init(2005);
    		int n = cin.nextInt(); int m = cin.nextInt();
    		System.out.println(get(n, m));
    	}
    }
    

你可能感兴趣的:(ACM)