BZOJ2219数论之神. 开k次根号

BZOJ2219. 数论之神

Q4.1.4.5. 开k次根号

先将模数 mod 分解为若干个 p^k
由题, p为奇质数
只需求 xA=B(modpk)的个数
设 p^k 的原根为 g, 由原根的性质, g 是 p 的原根

  1. B==0(modp^k) ,此时只需 x^A 中的 p 的个数 >= k, 要求 x 中 p 的个数 >= 上取整(k/A)
  2. (B, p^k)==1 ,此时取离散对数, 得到 A*ln(x)=ln(B) mod(phi(p^k)), 个数与其gcd有关
  3. 其余情况: 设 B=p^t*b, 则若 t%A!=0 则无解, 否则两边同时除以p^t, 则答案为情况2 * p^
点击查看代码

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <unordered_map>
#include <math.h>

using namespace std;

typedef long long LL;

LL qpow(LL b, LL p, LL mod) {
	LL res = 1;
	while(p) {
		if(p & 1) res = res * b % mod;
		b = b * b % mod, p >>= 1;
	}
	return res;
}
LL gcd(LL x, LL y) {
	while(x) {
		y %= x;
		x ^= y ^= x ^= y;
	}
	return y;
}
LL factors[10000];
LL get_g(LL p) { // 求原根
	LL cnt = 0, phi = p - 1;
	for(LL t = 2; t * t <= phi; t ++) // 求因数
		if(phi % t == 0) {
			factors[++ cnt] = t;
			if(t * t != phi) factors[++ cnt] = phi / t;
		}
	for(LL t = 2; t < p; t ++) { // 找原根
		bool st = true;
		for(LL i = 1; i <= cnt; i ++)
			if(qpow(t, factors[i], p) == 1) { st = false; break; }
		if(st) return t;
	}
	return 0; // 没有原根?
}

LL BSGS(LL a, LL b, LL p) { // BSGS 求 a^x = b (modp)
	unordered_map<LL, LL> hash;
	a %= p; LL t = sqrt(p) + 1;
	for(LL i = 0; i < t; i ++)
		hash[b * qpow(a, i, p) % p] = i;
	a = qpow(a, t, p);
	if(a == 0) { // 特判
		if(b == 0) return 1;
		return -1; // 无解
	}
	for(LL i = 1; i <= t; i ++) {
		LL val = qpow(a, i, p);
		if(!hash.count(val)) continue;
		LL j = hash.at(val);
		if(i * t - j >= 0) return i * t - j;
	}
	return -1;
}

// 求 x^A = B mod(sum = a**k) 的个数
LL solve(LL A, LL B, LL sum, LL a, LL k) { // sum = a**k
	B %= sum;
	if(!B) return qpow(a, k - ((k - 1) / A + 1), 1e9); // 情况1
	LL y = 0; // B 中 a 的个数
	while(B % a == 0) B /= a, y ++;
	if(y % A) return 0; // 无解
	LL g = get_g(a); // 找到原根
	LL m = sum - sum / a; // phi(sum = a**k)
	LL Gcd = gcd(A, m);
	if(BSGS(g, B, sum) % Gcd == 0) return Gcd * qpow(a, y - y / A, 1e9);
	else return 0; // 无解
}

int main() {
	int T;
	scanf("%d", &T);
	while(T --) {
		LL a, b, mod, ans = 1;
		scanf("%lld%lld%lld", &a, &b, &mod), mod = mod * 2 + 1;
		for(LL t = 2; t * t <= mod; t ++)
			if(mod % t == 0) {
				LL k = 0, pw = 1;
				while(mod % t == 0) k ++, pw *= t, mod /= t;
				ans = ans * solve(a, b, pw, t, k);
			}
		if(mod > 1) ans = ans * solve(a, b, mod, mod, 1);
		printf("%lld\n", ans);
	}
	return 0;
}
posted @ 2022-09-28 15:17  azzc  阅读(31)  评论(0编辑  收藏  举报