[bzoj2242][SDOI2011]计算器
题目大意:三合一,给你$y,z,p$,求$x$,三种询问
- $y^z\bmod{p}$
- $xy\equiv z\pmod p$的最小非负整数
- $y^z\equiv z\pmod p$的最小非负整数
题解:求快速幂,逆元和$BSGS(离散对数)$
$BSGS$就是用分块的思想,令$m=\lceil \sqrt p\rceil$,因为$y^{i\times m+j}=y^{i\times m}\times y^j$所以可以预处理$y^i$(用$hash$或$map(加复杂度和大常数,但是方便。。。)$ $(unordered\_map是C++11的,考前还是不要用了吧)$,然后枚举$i$,求出$y^{i\times m}$,找一下有没有对应的$y^j$就行了
卡点:无
C++ Code:
#include <cstdio> #include <cmath> #include <map> long long T, k; long long y, z, p; namespace calc1 { long long calc (long long base, long long p, long long mod) { base %= mod, p %= mod - 1; long long ans = 1; for (; p; p >>= 1, base = base * base % mod) if (p & 1) ans = ans * base % mod; return ans; } long long inv(long long a, long long mod) { return calc(a, mod - 2, mod); } } namespace calc2 { long long calc (long long y, long long z, long long mod) { y %= mod, z %= mod; if (!y) return z ? -1 : 0; long long tmp = calc1::inv(y, mod); return tmp * z % mod; } } namespace calc3 { std::map<int, int> mp; long long calc(long long y, long long z, long long mod) { y %= mod, z %= mod; if (!y) return -1; long long tmp = 1, t = sqrt(mod - 1) + 1; mp.clear(); for (int i = 0; i <= t; i++) { mp[tmp * z % mod] = i; if (i != t) tmp = tmp * y % mod; } long long tmp6 = tmp; for (int i = 1; i <= t; i++) { if (mp.count(tmp6)) return i * t - mp[tmp6]; tmp6 = tmp6 * tmp % mod; } return -1; } } int main() { scanf("%lld%lld", &T, &k); while (T --> 0) { scanf("%lld%lld%lld", &y, &z, &p); long long tmp; switch (k) { case 1: tmp = calc1::calc(y, z, p); break; case 2: tmp = calc2::calc(y, z, p); break; case 3: tmp = calc3::calc(y, z, p); break; } if (tmp == -1) puts("Orz, I cannot find x!"); else printf("%lld\n", tmp); } }