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

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

首先高次不定方程分为\(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;
}

posted @ 2018-11-16 12:46  mrclr  阅读(686)  评论(0编辑  收藏  举报