zoukankan      html  css  js  c++  java
  • 数论学习笔记之高次不定方程

    这几天沉迷在数论的海洋中(快淹死了)无法自拔……

    首先高次不定方程分为(A ^ x equiv B(mod C))(x ^ A equiv B(mod C))两种形式,对应的解法也不一样。今天先学了第一个。




    (A ^ x equiv B(mod C))
    首先得知道这么一回事儿,就是这个解是有周期的,且最大周期为(C)
    证明:由于(A ^ x mod C < C),所以最多只有(C)个元素,那么在(n(n leqslant C))之内,如果有(A ^ {x_0} equiv A ^ {x_0 + n} (mod C)),那么(A ^ {x_0} A ^ t equiv A ^ {x_0 + n} A ^ t(mod C)),既有(A ^ x equiv A ^ {x + n})对于所有(x)成立,且周期为(n)


    那么暴力的算法就很好写了:从(0)(C - 1)枚举,代入方程看是否成立。
    但是这个复杂度是(O(maxint * log(maxint)))的,过不去。
    于是就有了很奇葩的(Baby-step) (giant-step(BSGS))算法。能使时间复杂度达到(O(sqrt{n}))


    既然是(O(sqrt{n})),那自然想到了分块,也就是我们把枚举的数分成(sqrt{C})块,每一块里有(sqrt{C})
    个数。但这样还是(O(C))复杂度的,下面才是重点:
    枚举的时候看成(A ^ {im - y} equiv B(mod C)),其中(i)代表第几块,(m)是块的大小,然后在每一块内枚举(y)
    然后变个型:(A ^ {im} equiv A ^ y B (mod C))。这也就是说组数(i)和每一组内的(y)并没有关系,因此我们可以先从(0 ~ sqrt{C} - 1)枚举(y),将答案存在哈希表中(可(O(1))查询),然后在(O(sqrt{n}))枚举块数,看哈希表中有没有这个答案即可。


    但上述思路的前提是在(A)(C)互质的情况下。若(A)(C)不互质,只要刚开始把(A)(C)都不断除以(d_i = gcd(A, C_i)),直到(A)(C_n)互质为止。然后式子就变成了(B_n = frac{A ^ n}{d_1d_2 ldots d_n}A ^ {x - n} + C_ny),其中(D = frac{A ^ n}{d_1d_2 ldots d_n}),变个型:(A ^ {x - n} equiv B_nD ^ {-1}(mod C_n)),再像上面做即可。
    需要注意的是求(D ^ {-1})不能用费马小定理,因为(C_n)不一定为质数,只能用(exgcd)求解。
    (这好像就是传说中的(exbsgs)


    哈希表我还是现学的,其实就是对于每个模完后相同的数存到一块,用链前维护即可。
    题目链接

    #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 = 5e4 + 5;
    const int base = 999979;
    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');
    }
    
    struct Hash
    {
      int nxt, to, w;
    }e[maxn];
    int head[base], hcnt = 0;
    int st[maxn], top = 0;
    void init()
    {
      while(top) head[st[top--]] = 0;
      top = hcnt = 0;
    }
    void insert(int x, int y)
    {
      int h = x % base;
      if(!head[h]) st[++top] = h;
      e[++hcnt] = (Hash){head[h], x, y};
      head[h] = hcnt;
    }
    int query(int x)
    {
      int h = x % base;
      for(int i = head[h]; i; i = e[i].nxt)
        if(e[i].to == x) return e[i].w;
      return -1;
    }
    
    int gcd(int a, int b) {return b ? gcd(b, a % b) : a;}
    void exgcd(int a, int b, int& x, int& y)
    {
      if(!b) x = 1, y = 0;
      else exgcd(b, a % b, y, x), y -= (ll)x * (a / b);
    }
    int inv(int a, int mod)
    {
      int x, y;
      exgcd(a, mod, x, y);
      return (x % mod + mod) % mod;
    }
    
    int bsgs(int a, int b, int c)
    {
      int g, d = 1, cnt = 0;
      while((g = gcd(a, c)) != 1)
        {
          if(b % g) return -1;
          cnt++;
          b /= g; c /= g;
          d = (ll)d * (a / g) % c;
        }
      b = (ll)b * inv(d, c) % c;
      init();
      int s = sqrt(c), p = 1;
      for(int i = 0; i < s; ++i)
        {
          if(p == b) return i + cnt;
          insert((ll)p * b % c, i);
          p = (ll)p * a % c;
        }
      for(int i = s, q = p, t; i - s + 1 <= c - 1; i += s)
        {
          t = query(q);
          if(t != -1) return i - t + cnt;
          q = (ll)q * p % c;
        }
      return -1;
    }
    
    int X, Z, K;
    
    int main()
    {
      while(scanf("%d%d%d", &X, &Z, &K) && (ll)X + Z + K > 0)
        {
          X %= Z; K %= Z;
          int ans = bsgs(X, K, Z);
          if(ans == -1) puts("No Solution");
          else write(ans), enter;
        }
      return 0;
    }
    
    
  • 相关阅读:
    conn
    快速指数算法+Python代码
    扩展欧几里得算法+Python代码
    最速下降法+Matlab代码
    第二类生日攻击算法
    遗传算法+Python代码
    模糊聚类+Matlab代码
    数据库检索
    Spring Data Jpa依赖和配置
    上传Typora到博客园(解决图片缩放问题)
  • 原文地址:https://www.cnblogs.com/mrclr/p/9965886.html
Copyright © 2011-2022 走看看