BZOJ2219数论之神. 开k次根号
先将模数 mod 分解为若干个 p^k
由题, p为奇质数
只需求 xA=B(modpk)的个数
设 p^k 的原根为 g, 由原根的性质, g 是 p 的原根
- B==0(modp^k) ,此时只需 x^A 中的 p 的个数 >= k, 要求 x 中 p 的个数 >= 上取整(k/A)
- (B, p^k)==1 ,此时取离散对数, 得到 A*ln(x)=ln(B) mod(phi(p^k)), 个数与其gcd有关
- 其余情况: 设 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;
}