标签:
Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 2032 Solved: 791
[Submit][Status][Discuss]
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。
什么是BSGS呢?
他就是来解决③这种问题的。
我们要求y^x=z(mod p)
这里的x<=p-1。为什么呢?
因为由费马小定理可之当x=p-1时z=1。当p=0时z=1。
所以在小于p之内一定会出现循环节。
我们可以转化成求
y^(i*m+j)=z(mod p)
y^(i*m)=z * ni(y^j) (mod p) ni(x)表示x的逆元。
这里的m=sprt(p)。
我们只需要先预处理出y^j(1<=j<=sprt(p))
然后对于左边,我们枚举i,如果有符合的就输出,没有的话就说明不存在x。
但其实我们并不需要去求逆元。
我们只需要将i * m+j改成i * m - j,然后直接乘过去就好了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
using namespace std;
#define LL long long
int t,k;
map<LL,LL> mp;
LL quickpow(LL y,LL z,LL p)
{
LL ans=1;
while(z){
if(z&1) ans=ans*y%p;
z>>=1;
y=y*y%p;
}
return ans;
}
int main()
{
LL ans,y,z,p;
scanf("%d%d",&t,&k);
while(t--){
scanf("%lld%lld%lld",&y,&z,&p);
if(k==1) printf("%lld\n",quickpow(y,z,p));
if(k==2){
LL ni=quickpow(y,p-2,p);
ans=ni*z%p;
if(ans*y%p==z%p) printf("%lld\n",ans);
else printf("Orz, I cannot find x!\n");
}
if(k==3){
bool f=false;
LL i,j=1,m=sqrt(p);
y%=p,z%=p;
if(!y) printf("Orz, I cannot find x!\n");
else{
mp.clear();
mp[1]=0;
for(i=1;i<m;++i){
j=j*y%p;
if(!mp[j*z%p]) mp[j*z%p]=i;
}
j=1;
LL tmp=quickpow(y,m,p);
for(i=0;i<m;++i){
if(mp[j]){
f=true;
printf("%lld\n",i*m-mp[j]);
break;
}
j=j*tmp%p;
}
if(!f) printf("Orz, I cannot find x!\n");
}
}
}
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/fzhvampire/article/details/47444757