牛客——[SDOI2011]计算器(快速幂+扩展欧几里得+BSGS)
作者:互联网
链接:https://ac.nowcoder.com/acm/problem/20347
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
你被要求设计一个计算器完成以下三项任务:
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 1 2
示例2
输入
3 2 2 1 3 2 2 3 2 3 3
输出
2 1 0
备注:
【数据规模和约定】 对于100%的数据,1<=y,z,p<=10^9,为质数,1<=T<=10。
题意:1直接求值(2,3)求的是最小非负x
题解:
1.快速幂
2.扩展欧几里得,x*y%p=z 转换成 x*y=z+p*r 移项就是 x*y-p*r=z (x,r为未知数) 显然是扩展欧几里得的形式,就是扩展欧几里得求最小非负整数解,注意:传参要传正的p,代码里有注释
3.BSGS板子
这样我们就能写了,上代码:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <tr1/unordered_map>
using namespace std::tr1;
using namespace std;
typedef long long ll;
ll quick(ll a, ll b,ll c){
ll ans=1;
while(b){
if(b&1) ans=(ans*a)%c;
b>>=1;
a=(a*a)%c;
}
return ans;
}
ll ex_gcd(ll a, ll b, ll &x, ll &y){//ax+by=n
if(b==0){
x=1,y=0;
return a;
}
ll p=ex_gcd(b,a%b,y,x);
y-=a/b*x;
return p;
}
void exgcd(int a,int &x,int b,int &y){//ax+by=1
if(!b){
x=1;y=0;
return ;
}
exgcd(b,y,a%b,x);
y-=a/b*x;
}
int inverse(int x,int y){//x^(-1)(mod y) <=> x*x^(-1)+y*k=1
int inv_x,k;
exgcd(x,inv_x,y,k);
return (inv_x%y+y)%y;
}
int BSGS(int a,int b,int c){//a^x=b(mod c)
//特判答案<=100的情况
for(int x=0,pow_a_x=1%c;x<=100;++x){
if(pow_a_x==b)return x;
pow_a_x=(long long)pow_a_x*a%c;
}
//通过预处理使得a,c互质
int base_count=0,D=1;
while(1){
int d=__gcd(a,c);
if(d==1)break;
if(b%d)return -1;
b/=d;c/=d;
D=(long long)D*(a/d)%c;
++base_count;
}
b=(long long)b*inverse(D,c)%c;
//解a^(x-base_count)=b(mod c)
int n=sqrt(c);
unordered_map<int,int>hash_table;
int pow_a_j=1;
for (int j = 1; j <= n;++j){
pow_a_j=(long long)pow_a_j*a%c;
hash_table[(long long)pow_a_j*b%c]=j;
}
int pow_a_n=pow_a_j,pow_a_in=1,max_i=(c+n-1)/n;
for (int i = 1; i <= max_i;++i){
pow_a_in=(long long)pow_a_in*pow_a_n%c;
if(hash_table.count(pow_a_in)) return i*n-hash_table[pow_a_in]+base_count;
}
return -1;
}
int main(){
int t,k;
scanf("%d%d",&t,&k);
while(t--){
int y,z,p;
scanf("%d%d%d",&y,&z,&p);
if(k==1){
printf("%lld\n",quick(y,z,p));
}
else if(k==2){
//推出的式子是y*xx-p*yy=z,但是传参数的时候要用正的p!!!否则错误!
ll xx,yy;
xx=yy=0;
ll gcd=ex_gcd(y,p,xx,yy);
if(z%gcd!=0){
puts("Orz, I cannot find x!");
continue;
}
ll kk=z/gcd;
xx*=kk;
ll r1=p/gcd;
xx=(xx%r1+r1)%r1;
//if(x==0) x=r1;//求出非0的x的最小解的
printf("%lld\n",xx);
}
else{
int w=BSGS(y,z,p);
if(w==-1) puts("Orz, I cannot find x!");
else printf("%d\n",w);
}
}
return 0;
}
标签:include,return,牛客,int,ll,非负,SDOI2011,mod,BSGS 来源: https://blog.csdn.net/lgz0921/article/details/98470360