数论学习笔记之高次不定方程

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

首先高次不定方程分为\(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\)都不断除以\(gcd(A, C)\),直到\(A\)\(C\)互质为止,然后再像上面做即可。(这好像就是传说中的\(exbsgs\)


然而哈希表我不太会写,用\(map\)又gg了,所以这里贴了一个别人的代码……
题目链接

#include <cmath>
#include <cstdio>
int X, Y, Z, K;
int Gcd(int a, int b) { return !b ? a : Gcd(b, a % b); }
int exGcd(int a, int b, int &x, int &y) {
  if (b == 0) return x = 1, y = 0, a;
  int r = exGcd(b, a % b, y, x);
  y -= (long long)(a / b) * x;
  return r;
}
int inv(int a, int m) {
  int x, y;
  exGcd(a, m, x, y);
  return (x % m + m) % m;
}
namespace Hash {
  const int N = 50000;
  const int H = 999979;
  int tot, adj[H], nxt[N], num[N], val[N];
  int top, stk[N];
  void init() {
    tot = 0;
    while (top) adj[stk[top--]] = 0;
  }
  void insert(int x, int y) {
    int h = x % H;
    for (int e = adj[h]; e; e = nxt[e]) {
      if (num[e] == x) {
    val[e] = y;
    return ;
      }
    }
    if (!adj[h]) stk[++top] = h;
    nxt[++tot] = adj[h], adj[h] = tot;
    num[tot] = x, val[tot] = y;
  }
  int query(int x) {
    int h = x % H;
    for (int e = adj[h]; e; e = nxt[e])
      if (num[e] == x) return val[e];
    return -1;
  }
}
// a^x = b (mod c)
int BSGS(int a, int b, int c) {
  int cnt = 0, G, d = 1;
  while ((G = Gcd(a, c)) != 1) {
    if (b % G != 0) return -1;
    ++cnt, b /= G, c /= G;
    d = (long long)d * (a / G) % c;
  }
  b = (long long)b * inv(d, c) % c;
  Hash::init();
  int s = sqrt(c);
  int p = 1;
  for (int i = 0; i < s; ++i) {
    if (p == b) return i + cnt;
    Hash::insert((long long)p * b % c, i);
    p = (long long)p * a % c;
  }
  int q = p, t;
  for (int i = s; i - s + 1 <= c - 1; i += s) {
    t = Hash::query(q);
    if (t != -1) return i - t + cnt;
    q = (long long)q * p % c;
  }
  return -1;
}
bool check() {
  for (int i = 0, j = 1; i <= 10; ++i) {
    if (j == K) {
      printf("%d\n", i);
      return true;
    }
    j = (long long)j * X % Z;
  }
  if (X == 0) {
    puts("No Solution");
    return true;
  }
  return false;
}

int main() {
  // X^Y = K (mod Z)
  while (scanf("%d%d%d", &X, &Z, &K), (long long)X + Z + K > 0){
    X %= Z, K %= Z;
    if (check()) continue;
    int ans = BSGS(X, K, Z);
    if (ans == -1) puts("No Solution");
    else printf("%d\n", ans);
  }
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/mrclr/p/9965886.html