zoukankan      html  css  js  c++  java
  • 数论学习笔记之一元线性同余方程组

    如题,解法主要是合并法和中国剩余定理。
    而且据我所知,合并法好像就是扩展中国剩余定理……




    一元线性同余方程组就是一堆形如 (x equiv a_1(mod m_1))的同余方程,然后一般让你求出一个最小正整数解。


    算法1 合并法
    也就是我们将方程两两合并,然后求出合并后的解,从而推出最终解。实现的时候实际上是累加的合并,就是如果有三个方程(s1, s2, s3),那么先算出(s1, s2)合并后的解,然后得到一个新方程再和(s3)合并,再解出这两个方程的解。
    那么就以两个方程为例吧:
         (x equiv r_1 (mod a_1))
         (x equiv r_2 (mod a_2))
    首先展开得:
         (x = r_1 + y_1a_1)
         (x = r_2 + y_2a_2)
    这个时候我们想:如果只有第一个方程,那么答案就是(r_1),但现在有两个,因此要联立:
         (r_1 + y_1a_1 = r_2 + y_2a_2)
         (y_1a_1 - y_2a_2 = r_2 - r_1)
    对于这个方程,我们只要解出最小的正整数解(y_1),则答案就是(r_1 + y_1a_1)。因为首先解出的解一定是同时满足这两个方程的,然后让答案为正整数,且最小,那么(y_1)就要是最小正整数解。


    至于解这个方程,用(exgcd)就行了,这里不在赘述。需要注意的是如果(r_2 - r_1)不能整除(gcd(a_1, a_2)),说明方程组误无解。


    那么到现在就成功合并了两个方程,接下来讲一下怎么得到新的方程:
    假如这两个方程的最小正整数解是(x'),那么他的通解是(x' + k * lcm(a_1, a_2)),也就可以写成(x equiv x' (mod lcm(a_1, a_2))),于是这就是新的方程了!然后拿这个方程再和别的方程合并即可。
    最后的(r_1)就是答案。


    附上例题和代码:poj 2891

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<vector>
    #include<stack>
    #include<queue>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define rg register
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const db eps = 1e-8;
    //const int maxn = ;
    inline ll read()
    {
      ll ans = 0;
      char ch = getchar(), last = ' ';
      while(!isdigit(ch)) last = ch, ch = getchar();
      while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
      if(last == '-') ans = -ans;
      return ans;
    }
    inline void write(ll x)
    {
      if(x < 0) x = -x, putchar('-');
      if(x >= 10) write(x / 10);
      putchar(x % 10 + '0');
    }
    
    int n; 
    
    void exgcd(ll a, ll b, ll& d, ll& x, ll& y)
    {
      if(!b) d = a, x = 1, y = 0;
      else exgcd(b, a % b, d, y, x), y -= a / b * x;
    }
    
    int main()
    {
      while(scanf("%d", &n) != EOF)
        {
          bool flg = 1;
          ll a1 = read(), r1 = read(), a2, r2, a, b, c, d, x, y;
          for(int i = 1; i < n; ++i)
    	{
    	  a2 = read(); r2 = read();
    	  a = a1; b = a2; c = r2 - r1;
    	  exgcd(a, b, d, x, y);
    	  if(c % d) flg = 0;
    	  ll t = b / d;
    	  x = (x * (c / d) % t + t) % t;
    	  r1 += a1 * x;    //更新余数
    	  a1 *= a2 / d;    //更新模数,那么新的方程就是x = r1 + y1 * a1
    	}
          if(!flg) puts("-1");
          else write(r1), enter;
        }
      return 0;
    }
    


    算法2:**中国剩余定理** 我以前有一篇博客好像也讲过,然而现在早忘了,于是这里再叨叨一遍。 至于这个算法是怎么想出来的,我也不知道。 令$M = prod_{i = 1} ^ {n}{m_i}$,$M_i = M / m_i$,那么每一个方程就变成了$M_iw_i equiv a_i(mod m_i)$,于是我们求出$w_i$就行。然而这很不好求,所以先求出$M_it_i equiv 1 (mod m_i)$的解$t_i$,则$w_i = t_ia_i$。 最终的解$x = sum_{i = 1} ^ {n}{a_iM_it_i}$。 例题就找了人人皆知的[曹冲养猪](https://www.luogu.org/problemnew/show/P1495) ```c++ #include #include #include #include #include #include #include #include #include #include using namespace std; #define enter puts("") #define space putchar(' ') #define Mem(a, x) memset(a, x, sizeof(a)) #define rg register typedef long long ll; typedef double db; const int INF = 0x3f3f3f3f; const db eps = 1e-8; const int maxn = 12; inline ll read() { ll ans = 0; char ch = getchar(), last = ' '; while(!isdigit(ch)) last = ch, ch = getchar(); while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar(); if(last == '-') ans = -ans; return ans; } inline void write(ll x) { if(x < 0) x = -x, putchar('-'); if(x >= 10) write(x / 10); putchar(x % 10 + '0'); }

    int n;
    ll l[maxn], b[maxn], p[maxn], multi[maxn], M = 1;
    ll x, y;
    ll ans = 0;
    void exgcd(ll a, ll b, ll& x, ll& y)
    {
    if(!b) x = 1, y = 0;
    else exgcd(b, a % b, y, x), y -= x * (a / b);
    }
    int main()
    {
    n = read();
    for(int i = 1; i <= n; ++i)
    {
    p[i] = read(); b[i] = read();
    M *= p[i];
    }
    for(int i = 1; i <= n; ++i)
    {
    ll m = M / p[i];
    exgcd(m, p[i], x, y);
    x = (x + p[i]) % p[i];
    ll t = m * x;
    ans += (t * b[i]);
    }
    ans %= M;
    write(ans), enter;
    }

    </br>
    </br>
    [祭]终于把一元线性同余方程懂了……
  • 相关阅读:
    高斯消元学习
    HDU 4596 Yet another end of the world(解一阶不定方程)
    Codeforces Round #318 div2
    HDU 4463 Outlets(一条边固定的最小生成树)
    HDU 4458 Shoot the Airplane(计算几何 判断点是否在n边形内)
    HDU 4112 Break the Chocolate(简单的数学推导)
    HDU 4111 Alice and Bob (博弈)
    POJ 2481 Cows(线段树单点更新)
    HDU 4288 Coder(STL水过)
    zoj 2563 Long Dominoes
  • 原文地址:https://www.cnblogs.com/mrclr/p/9963097.html
Copyright © 2011-2022 走看看