题目描述
你被要求设计一个计算器完成以下三项任务:
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”之间有一个空格。
样例输入
【样例输入1】
3 1
2 1 3
2 2 3
2 3 3
【样例输入2】
3 2
2 1 3
2 2 3
2 3 3
样例输出
【样例输出1】
2
1
2
【样例输出2】
2
1
0
题解
对于第一种情况,直接上快速幂就可以。
对于第二种情况,我们用扩展欧几里得算法,
xy−kp=z
x
y
−
k
p
=
z
,求出x之后转换为最小
对于第三种情况,直接BSGS就可以
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<map>
#define ll long long
using namespace std;
map<ll,int> vis;
int T,opt;
ll y,z,p;
ll qsm(ll a,ll b,ll mod)
{
ll sum=1;
while(b)
{
if(b&1) sum=(sum*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return sum;
}
void Ex_Gcd(ll a,ll b,ll &x,ll &y)
{
if(!b)
{
x=1;y=0;
return ;
}
Ex_Gcd(b,a%b,x,y);
ll temp=x;
x=y;
y=temp-(a/b*y);
return ;
}
ll Gcd(ll a,ll b)
{
if(!b) return a;
else return Gcd(b,a%b);
}
ll solve_2(ll a,ll b,ll c)//Ex_Gcd
{
a%=c;
b%=c;
ll now=Gcd(a,c);
ll x,y;
if(b%now!=0) return -1;
a/=now,b/=now,c/=now;
Ex_Gcd(a,c,x,y);
x*=b;
while(x<0) x+=c;
while(x-c>=0) x-=c;//求最小非负的x
return x;
}
ll solve_3(ll a,ll b,ll mod)//BSGS
{
vis.clear();
if(a%mod==0) return -1;
else
{
ll ans;
int m=1+(sqrt(mod));
for(int i=0;i<=m;i++)
{
if(!i)
{
ans=b%mod;
vis[ans]=i;
continue ;
}
ans=(ans*a)%mod;
vis[ans]=i;
}
ll t=qsm(a,m,mod);ans=1;
for(int i=1;i<=m;i++)
{
ans=(ans*t)%mod;
if(vis[ans])
return (i*m-vis[ans]);
}
return -1;
}
}
int main()
{
scanf("%d%d",&T,&opt);
while(scanf("%lld%lld%lld",&y,&z,&p)&&T)
{
T--;
if(opt==1)
{
printf("%lld\n",qsm(y,z,p)%p);
}
if(opt==2)
{
ll ans=solve_2(y,z,p);
if(ans!=-1) printf("%lld\n",ans);
else printf("Orz, I cannot find x!\n");
}
if(opt==3)
{
ll ans=solve_3(y,z,p);
if(ans!=-1) printf("%lld\n",ans);
else printf("Orz, I cannot find x!\n");
}
if(!T) break;
}
return 0;
}