[bzoj2242][SDOI2011]计算器

题目大意:三合一,给你$y,z,p$,求$x$,三种询问

  1. $y^z\bmod{p}$
  2. $xy\equiv z\pmod p$的最小非负整数
  3. $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);
	}
}

 

posted @ 2018-09-01 20:39  Memory_of_winter  阅读(133)  评论(0编辑  收藏  举报