其他分享
首页 > 其他分享> > 牛客——[SDOI2011]计算器(快速幂+扩展欧几里得+BSGS)

牛客——[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