【bzoj2242】[SDOI2011]计算器 EXgcd+BSGS

题目描述

你被要求设计一个计算器完成以下三项任务:
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


题解

EXgcd+BSGS

第一问直接快速幂。

第二问需要将xy≡z(mod p)转化为xy+tp=z,进而用EXgcd求解。

第三问是裸的BSGS。

根据费马小定理可知如果有解,答案一定小于p。

设m=√p(向上取整),再设x=km+b,其中k<m,b<m。

那么就有y^(km+b)≡z(mod p),即y^b≡z/y^km(mod p)。

于是我们可以将所有的y^b mod p加入到map中,然后枚举k,求出z/y^km,看是否有相同的值在map中即可。

本题特判比较多,具体详见代码。

#include <cstdio>
#include <cmath>
#include <map>
using namespace std;
typedef long long ll;
map<ll , ll> f;
map<ll , ll>::iterator it;
ll pow(ll x , ll y , ll mod)
{
	ll ans = 1;
	while(y)
	{
		if(y & 1) ans = ans * x % mod;
		x = x * x % mod , y >>= 1;
	}
	return ans;
}
ll gcd(ll a , ll b)
{
	return b ? gcd(b , a % b) : a;
}
void exgcd(ll a , ll b , ll &x , ll &y)
{
	if(!b)
	{
		x = 1 , y = 0;
		return;
	}
	exgcd(b , a % b , x , y);
	ll t = x;
	x = y , y = t - a / b * y;
}
int main()
{
	int T , k;
	scanf("%d%d" , &T , &k);
	while(T -- )
	{
		ll y , z , p;
		scanf("%lld%lld%lld" , &y , &z , &p);
		switch(k)
		{
			case 1: printf("%lld\n" , pow(y , z , p)); break;
			case 2:
			{
				y %= p , z %= p; 
				ll t = gcd(y , p) , x1 , x2;
				if(z % t != 0)
				{
					printf("Orz, I cannot find x!\n");
					break;
				}
				y /= t , p /= t , z /= t , exgcd(y , p , x1 , x2) , x1 *= z;
				while(x1 < 0) x1 += p;
				while(x1 - p >= 0) x1 -= p;
				printf("%lld\n" , x1);
				break;
			}
			default:
			{
				y %= p , z %= p; 
				if(!y)
				{
					if(!z) printf("1\n");
					else printf("Orz, I cannot find x!\n");
					break;
				}
				ll m = (ll)ceil(sqrt(p)) , i , flag = 0 , t = 1 , temp;
				f.clear();
				for(i = 0 ; i < m ; i ++ )
				{
					if(f.find(t) == f.end()) f[t] = i;
					t = t * y % p;
				}
				temp = pow(y , p - m - 1 , p) , t = 1;
				for(i = 0 ; i <= m ; i ++ )
				{
					it = f.find(z * t % p) , t = t * temp % p;
					if(it != f.end())
					{
						printf("%lld\n" , i * m + it->second) , flag = 1;
						break;
					}
				}
				if(!flag) printf("Orz, I cannot find x!\n");
			}
		}
	}
	return 0;
}

 

 

posted @ 2017-06-13 09:14  GXZlegend  阅读(353)  评论(0编辑  收藏  举报