您的位置:首页 > 其它

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

2017-06-13 09:14 405 查看
题目描述

你被要求设计一个计算器完成以下三项任务:
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: