洛谷传送门
BZOJ传送门
题目描述
你被要求设计一个计算器完成以下三项任务:
1、给定 y、z、p y 、 z 、 p ,计算 yz mod p y z m o d p 的值;
2、给定 y、z、p y 、 z 、 p ,计算满足 xy≡z(mod p) x y ≡ z ( m o d p ) 的最小非负整数x;
3、给定 y、z、p y 、 z 、 p ,计算满足 yx≡z(mod p) y x ≡ z ( m o d p ) 的最小非负整数x。
为了拿到奖品,全力以赴吧!
输入输出格式
输入格式:
输入文件calc.in 包含多组数据。
第一行包含两个正整数 T T 、,分别表示数据组数和询问类型(对于一个测试点内的所有数据,询问类型相同)。
以下T 行每行包含三个正整数 y、z、p y 、 z 、 p ,描述一个询问。
输出格式:
输出文件calc.out 包括 T T 行.
对于每个询问,输出一行答案。
对于询问类型 和
3
3
,如果不存在满足条件的,则输出Orz, I cannot find x!
。
输入输出样例
输入样例#1:
3 1
2 1 3
2 2 3
2 3 3
输出样例#1:
2
1
2
输入样例#2:
3 2
2 1 3
2 2 3
2 3 3
输出样例#2:
2
1
0
输入样例#3:
4 3
2 1 3
2 2 3
2 3 3
2 4 3
输出样例#3:
0
1
Orz, I cannot find x!
0
说明
解题分析
对于第一个操作, 直接快速幂就可以了。
第二操作我们可以用直接解方程解决。
第三个操作我们就要用 BSGS B S G S (北上广深)解决了。
其实这玩意就是个分块优化的暴力, 设 seg=P−−√ s e g = P , 因为 Aϕ(P)≡1(mod P) A ϕ ( P ) ≡ 1 ( m o d P ) , 所以 x<P x < P , 那么就可以写成 Ak×seg+m≡B(mod P) A k × s e g + m ≡ B ( m o d P ) , 我们就可以先预处理 Am(m∈[0,seg) A m ( m ∈ [ 0 , s e g ) ,放在 map m a p 中, 再算出 Aseg×i A s e g × i , 用 exgcd e x g c d 解出对应的 Am A m , 查表即可。
总复杂度 O(MOD−−−−−√log(MOD−−−−−√)) O ( M O D l o g ( M O D ) ) 。 当然如果手写 hashmap h a s h m a p , 把 Am A m 移项可以做到 O(MOD−−−−−√) O ( M O D ) 。
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <map>
#define R register
#define IN inline
#define W while
#define gc getchar()
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
std::map <int, int> mp;
IN int fpow(R int x, R int tim, R int mod)
{
int ret = 1;
W (tim)
{
if(tim & 1) ret = 1ll * ret * x % mod;
x = 1ll * x * x % mod, tim >>= 1;
}
return ret;
}
int exgcd(R int a, R int b, int &x, int &y)
{
if(!b) return x = 1, y = 0, a;
int ret = exgcd(b, a % b, x, y);
int buf = x; x = y, y = buf - 1ll * a / b * y;
return ret;
}
int BSGS(R int A, R int B, R int MOD)
{
mp.clear();
if(MOD == 1) if(!B) return 0; else return -1;//几种特判
if(B == 1) if(A) return 0; else return -1;
if(!(A % MOD)) if(!B) return 1; else return -1;
int bd = std::ceil(std::sqrt(MOD));
int seg = 1, now = 1;
for (R int i = 0; i < bd; ++i)
{
if(!mp.count(seg)) mp[seg] = i;//可能mp[seg]=0, 用count来验证
seg = 1ll * seg * A % MOD;
}
int gcd, x, y;
for (R int i = 0; i < bd; ++i)
{
gcd = exgcd(now, MOD, x, y);
x = (1ll * x * B % MOD + MOD) % MOD;
if(mp.count(x)) return i * bd + mp[x];//
now = 1ll * now * seg % MOD;
}
return -1;
}
int main(void)
{
int a, b, c;
int T, typ, gcd, tim, ans, x, y;
in(T), in(typ);
W (T--)
{
in(a), in(b), in(c);
if(typ == 1) printf("%d\n", fpow(a, b, c));
else if(typ == 2)
{
gcd = exgcd(a, c, x, y);
if(b % gcd) {puts("Orz, I cannot find x!"); continue;}
tim = b / gcd; c /= gcd;
printf("%lld\n", (1ll * x * tim % c + c) % c);
}
else
{
ans = BSGS(a, b, c);
if(ans < 0) puts("Orz, I cannot find x!");
else printf("%d\n", ans);
}
}
}