[BZOJ2242][SDOI2011]计算器
试题描述
你被要求设计一个计算器完成以下三项任务:
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)的最小非负整数。
输入
输入包含多组数据。
第一行包含两个正整数T,K分别表示数据组数和询问类型(对于一个测试点内的所有数据,询问类型相同)。
以下行每行包含三个正整数y,z,p,描述一个询问。
输出
对于每个询问,输出一行答案。对于询问类型2和3,如果不存在满足条件的,则输出“Orz, I cannot find x!”,注意逗号与“I”之间有一个空格。
输入示例
3 1 2 1 3 2 2 3 2 3 3
输出示例
2 1 2
数据规模及约定
对于100%的数据,1<=y,z,p<=10^9,为质数,1<=T<=10。
题解
第一问快速幂,第二问求逆元,第三问用 BSGS。
BSGS 就是中途相遇的一个应用,我们可以令 x = km + b,于是有 ykm Ξ z·y-b (mod p),那么当 m = sqrt(n) 时,k 和 b 都不超过 sqrt(n),所以我们先枚举 b,然后把所有的 z·y-b 扔进 hash,然后再枚举 k 看看能不能匹配上。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> #include <cmath> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define LL long long #define oo 2147483647 LL Pow(int a, int b, int p) { LL ans = 1, t = a; while(b) { if(b & 1) (ans *= t) %= p; (t *= t) %= p; b >>= 1; } return ans; } void gcd(int a, int b, LL& x, LL& y) { if(!b){ x = 1; y = 0; return ; } gcd(b, a % b, y, x); y -= a / b * x; return ; } LL Inv(int y, int p) { if(y % p == 0) return -233; LL x, k; gcd(y, p, x, k); return (x % p + p) % p; } #define maxn 100010 #define MOD 100007 int ToT, head[maxn], nxt[maxn], num[maxn]; LL val[maxn]; int Find(LL x) { int u = x % MOD; for(int e = head[u]; e; e = nxt[e]) if(val[e] == x) return num[e]; return -233; } void Insert(LL x, int v) { int u = x % MOD; nxt[++ToT] = head[u]; val[ToT] = x; num[ToT] = v; head[u] = ToT; return ; } int main() { int T = read(), K = read(); while(T--) { int y = read(), z = read(), p = read(); if(K == 1) printf("%d ", (int)Pow(y, z, p)); if(K == 2) { LL tmp = Inv(y, p); if(tmp < 0) puts("Orz, I cannot find x!"); else printf("%d ", (int)(tmp * z % p)); } if(K == 3) { if(y % p == 0){ puts("Orz, I cannot find x!"); continue; } int m = sqrt(p); memset(head, 0, sizeof(head)); ToT = 0; LL val = z % p, invy = Inv(y, p); for(int i = 0; i < m; i++, (val *= invy) %= p) if(Find(val) < 0) Insert(val, i); val = 1; bool ok = 0; for(int i = 0; i * m <= p; i++, (val *= Pow(y, m, p)) %= p) if(Find(val) >= 0) { printf("%d ", i * m + Find(val)); ok = 1; break; } if(!ok) puts("Orz, I cannot find x!"); } } return 0; }