这几天沉迷在数论的海洋中(快淹死了)无法自拔……
首先高次不定方程分为(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;
}