扩展 BSGS

Posted _LPF_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了扩展 BSGS相关的知识,希望对你有一定的参考价值。

首先你要知道什么是 BSGS。

BSGS 用于求解形如 \\(a^x\\equiv b\\pmod p\\) 的高次同余方程,其中保证 \\(p\\) 为质数。

根据费马小定理定理,当 \\(p\\) 为质数时,\\(a^{p-1}\\equiv 1\\pmod p\\)

所以当 \\(x\\) 有解 \\(>p-1\\) 时,也必然有 \\(y=x-(p-1)\\) 为方程的解,换言之,保证 \\(0\\sim p-1\\) 内有解,或者无解。

\\(t=\\sqrt p\\),假设解为 \\(t\\times i-j\\)\\(i,j\\) 的取值均只有 \\(t\\) 种,可以事先枚举存入 Hash_Table / Map 里。

因为 \\(a^{t\\times i-j}\\equiv b\\pmod p\\),所以有 \\(a^{t\\times i}\\equiv b\\times a^j\\pmod p\\),再次枚举 \\(j\\) 的取值后在之前的表中查找即可。

时间复杂度就为 \\(O(\\sqrt p)\\),当然用 Map 带个 \\(\\log\\)

int BSGS(int a, int b, int p){
	Hash.clear();
	int t = (int)sqrt(p) + 1;
	b %= p;
	for(int i = 0; i < t; i ++){
		int val = 1LL * b * Pow(a, i, p) % p;
		Hash.insert(val, i);
	}
	a = Pow(a, t, p);
	if(a == 0) return b == 0 ? 1 : -1;
	for(int i = 0; i <= t; i ++){
		int val = Pow(a, i, p);
		int j = Hash.find(val);
		if(j >= 0 && i * t - j >= 0) return i * t - j;
	}
	return -1;
}

扩展 BSGS,即不保证 \\(p\\) 为素数。

假设 \\(d=\\gcd(a,p)>1\\),若 \\(b\\ {\\rm mod}\\ p\\not = 0\\),就无解。

否则因为 \\(a\\times d\\equiv b\\times d\\pmod {p\\times d}\\) 等价于 \\(a\\equiv b\\pmod {p}\\),可以直接消去。

直到 \\(a,p\\) 互质,假设总共用了 \\(k\\)\\(a\\),那 \\(k\\)\\(a\\) 消去后的乘积为 \\(A\\),除后的 \\(b,p\\)\\(B,P\\),则问题等价于。

\\(A\\times a^{x-k}\\equiv B\\pmod P\\),再转化一下变为 \\(a^{x-k}\\equiv B\\times Inv(A,P)\\pmod P\\)。(\\(Inv(A,P)\\) 表示 \\(A\\)\\({\\rm mod}\\ P\\) 意义下的逆元)

这是标准的 BSGS。

模板题放这,卡常卡的人都傻掉了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

const int N = 1e6 + 10;
const int MOD = 999991;
int a, p, b;

inline int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < \'0\' || c > \'9\') f = (c == \'-\') ? -1 : 1, c = getchar();
	while(c >= \'0\' && c <= \'9\') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

struct Hash_Table{
	int cnt, head[N], nxt[N], val[N], num[N];
	 
	void clear(){
		cnt = 0;
		memset(head, 0, sizeof(head));
	}
	
	void insert(int v, int k){
		int u = v % MOD + 1;
		for(int i = head[u]; i; i = nxt[i])
			if(val[i] == v){num[i] = k; return;}
		
		nxt[++ cnt] = head[u];
		val[cnt] = v, num[cnt] = k;
		head[u] = cnt;
	}
	
	int find(int v){
		int u = v % MOD + 1;
		for(int i = head[u]; i; i = nxt[i])
			if(val[i] == v) return num[i];
		return -1;
	}
} Hash;

inline int Gcd(int a, int b){
	while(b){
		int c = a;
		a = b, b = c % b;
	}
	return a;
}

inline int exGcd(int a, int b, int &x, int &y){
	if(!b){x = 1, y = 0; return a;}
	int d = exGcd(b, a % b, x, y);
	int z = x; x = y, y = z - a / b * y;
	return d;
}

inline int Pow(int a, int b, int p){
	int sum = 1;
	for(; b; b >>= 1){
		if(b & 1) sum = 1LL * sum * a % p;
		a = 1LL * a * a % p;
	}
	return sum;
}

inline int Inv(int a, int p){
	int x, y;
	exGcd(a, p, x, y);
	return (x % p + p) % p;
}

inline int BSGS(int a, int b, int p){
	Hash.clear();
	int t = (int)sqrt(p) + 1;
	b %= p;
	for(int i = 0; i < t; i ++){
		int val = 1LL * b * Pow(a, i, p) % p;
		Hash.insert(val, i);
	}
	a = Pow(a, t, p);
	if(a == 0) return b == 0 ? 1 : -1;
	for(int i = 0; i <= t; i ++){
		int val = Pow(a, i, p);
		int j = Hash.find(val);
		if(j >= 0 && i * t - j >= 0) return i * t - j;
	}
	return -1;
}

inline int exBSGS(int a, int b, int p){
	a %= p, b %= p;
	if(b == 1 || p == 1) return 0;
	int k = 0, d, A = 1;
	while((d = Gcd(a, p)) > 1){
		if(b % d != 0) return -1;
		k ++;
		b /= d, p /= d, A = 1LL * A * (a / d) % p;
		if(A == b) return k;
	}

	int ans = BSGS(a, 1LL * b * Inv(A, p) % p, p);
	if(ans == -1) return -1;
	return ans + k;
}

int main(){
	a = read(), p = read(), b = read();
	while(a){
		int ans = exBSGS(a, b, p);
		if(ans == -1) puts("No Solution");
		else printf("%d\\n", ans);
		a = read(), p = read(), b = read();
	}
	return 0;
}

以上是关于扩展 BSGS的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ_2242_[SDOI2011]计算器_快速幂+扩展GCD+BSGS

POJ 3243 Clever Y 扩展BSGS

扩展BSGS

模板扩展 BSGS/exBSGS

Clever Y POJ - 3243 (扩展BSGS)

BSGS(扩展篇,思路+详解)