题目
2242: [SDOI2011]计算器
Time Limit: 10 Sec Memory Limit: 512 MB
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,描述一个询问。对于100%的数据,1<=y,z,p<=10^9,为质数,1<=T<=10。
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 3Sample Output
【样例输出1】
2
1
2【样例输出2】
2
1
0
分析
- 第一种询问,直接快速幂;
- 第二种,x=(Y/1)*Z,逆元一下再模一下(求出最小解);
- 第三种,bsgs 求解即可。
程序
#include <cstdio>
#include <cmath>
#include <cstring>
#include <map>
using namespace std;
typedef long long ll;
ll m,T,K,Y,Z,P,k,F,G,ret,ss;
map <ll,ll> M;
ll ksm(ll x,ll y){
for (ret=1; y; y>>=1,x=(x*x)%P)
if (y&1) ret=(ret*x)%P;
return ret;
}
int main(){
scanf("%lld%lld",&T,&K);
if (K==1){
for (; T--; ){
scanf("%lld%lld%lld",&Y,&Z,&P);
printf("%lld\n",ksm(Y,Z));
}
return 0;
}
if (K==2){
for (; T--; ){
scanf("%lld%lld%lld",&Y,&Z,&P);
Y%=P,Z%=P;
if (Y==0 && Z!=0){
puts("Orz, I cannot find x!"); continue;}
k=(ksm(Y,P-2)*Z)%P;
printf("%lld\n",k);
}
return 0;
}
for (; T--; ){
scanf("%lld%lld%lld",&Y,&Z,&P);
Y%=P,Z%=P; //这里要模够
if (!Y && !Z){
puts("1"); continue;}
if (!Y){
puts("Orz, I cannot find x!"); continue;}
M.clear();
m=ceil(sqrt(P));
ss=Z%P; M[ss]=0;
for (ll i=1; i<=m; i++){
ss=(ss*Y)%P;
if (!M[ss]) M[ss]=i;
}
G=ksm(Y,m);
F=0,ss=1;
for (ll i=1; i<=m && !F; i++){
ss=(ss*G)%P;
if (M[ss]){
ss=(i*m-M[ss]);
printf("%lld\n",(ss%P+P)%P);
F=1;
break;
}
}
if (!F) puts("Orz, I cannot find x!");
}
return 0;
}
提示
- 打注释那里要模,不然过不了,不知道为什么。