bzoj 2242: [SDOI2011]计算器
Description
你被要求设计一个计算器完成以下三项任务:
1、给定y,z,p,计算Y^Z Mod P 的值;
2、给定y,z,p,计算满足xy≡ Z ( mod P )的最小非负整数;
3、给定y,z,p,计算满足Y^x ≡ Z ( mod P)的最小非负整数。
Input
输入包含多组数据。
第一行包含两个正整数T,K分别表示数据组数和询问类型(对于一个测试点内的所有数据,询问类型相同)。
以下行每行包含三个正整数y,z,p,描述一个询问。
Output
对于每个询问,输出一行答案。对于询问类型2和3,如果不存在满足条件的,则输出“Orz, I cannot find x!”,注意逗号与“I”之间有一个空格。
Sample Input
【样例输入1】
3 1
2 1 3
2 2 3
2 3 3
【样例输入2】
3 2
2 1 3
2 2 3
2 3 3
【数据规模和约定】
对于100%的数据,1<=y,z,p<=10^9,为质数,1<=T<=10。
Sample Output
【样例输出1】
2
1
2
【样例输出2】
2
1
0
知识点:BSGS算法
据说这是meet in the middle的一种应用。
关于meet in the middle可以看这里Meet-in-the-middle思想的一些应用
这只是一种思想,顾名思义,就是通过转化让单向的问题变为双向然后相遇得到答案。
比如说图论里面的双向广搜,还有就是等式的变换。
通常可以缩小到
O(f(n)−−−−√)
O
(
f
(
n
)
)
的时间复杂度
bsgs就是用来解决
Ax≡B(modP)
A
x
≡
B
(
mod
P
)
的问题的。
它有一个很中二的名字叫做拔山盖世算法。
做法是,设
x=k⋅m+r
x
=
k
⋅
m
+
r
AkmAr≡B(modP)
A
k
m
A
r
≡
B
(
mod
P
)
Ar≡B⋅inv(Akm)(modP)
A
r
≡
B
⋅
i
n
v
(
A
k
m
)
(
mod
P
)
由费马小定理可得
inv(Am)≡Ap−m−1(modP)
i
n
v
(
A
m
)
≡
A
p
−
m
−
1
(
mod
P
)
所以得到
inv(Akm)≡inv(A(k−1)m)⋅Ap−m−1(modP)
i
n
v
(
A
k
m
)
≡
i
n
v
(
A
(
k
−
1
)
m
)
⋅
A
p
−
m
−
1
(
mod
P
)
然后由MITM思想,把
AmodP
A
mod
P
扔到Hash表里面,枚举k即可。
然后m取
P−−√
P
复杂度比较优秀
题目分析
第一问第二问快速幂,第三问BSGS即可。注意考虑无解的情况。
代码
/**************************************************************
Problem: 2242
User: 2014lvzelong
Language: C++
Result: Accepted
Time:2016 ms
Memory:3284 kb
****************************************************************/
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
using namespace std;
map<int, int>mp;
int read() {
char ch = getchar(); int x = 0, f = 1;
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
int pow(int x, int k, int p) {
int b = 1;
for(;k; x = 1LL * x * x % p, k >>= 1) if(k & 1) b = 1LL * b * x % p;
return b;
}
void BSGS(int y, int z, int p) {
if(!y && !z) {puts("1"); return;}
if(!y) {puts("Orz, I cannot find x!"); return;}
mp.clear();
int m = ceil(sqrt(p)), t = 1;
mp[1] = m + 1;
for(int i = 1;i < m; ++i) {
t = 1LL * t * y % p;
if(!mp[t]) mp[t] = i;
}
int e = pow(y, p - m - 1, p), inv = 1;
for(int i = 0;i < m; ++i, inv = 1LL * inv * e % p) {
int r = mp[1LL * z * inv % p];
if(r) {
if(r == m + 1) r = 0;
printf("%lld\n", 1LL * i * m + r);
return ;
}
}
puts("Orz, I cannot find x!");
}
int main() {
int n = read(), t = read();
while(n--) {
int y = read(), z = read(), p = read(); y %= p;
if(t == 1) printf("%d\n", pow(y, z, p));
if(t == 2) {
z %= p;
if(!y && z) puts("Orz, I cannot find x!");
else printf("%d\n", 1LL * z * pow(y, p - 2, p) % p);
}
if(t == 3) BSGS(y, z % p, p);
}
return 0;
}