第一次写博客!!!!如有问题还请见谅!!!
题目描述
原题来自:SDOI 2011
你被要求设计一个计算器完成以下三项任务:
- 给定 y,z,p,计算 y^z mod p 的值;
- 给定 y,z,p, 计算满足 x×y≡z (modp ) 的最小非负整数 x;
- 给定 y,z,p,计算满足y^x≡z (modp ) 的最小非负整数 x。
输入
输入包含多组数据。
第一行包含两个正整数 T,K 分别表示数据组数和询问类型(对于一个测试点内的所有数据,询问类型相同);
以下 T 行每行包含三个正整数 y,z,p,描述一个询问。输出
对于每个询问,输出一行答案。
对于询问类型 2 和 3,如果不存在满足条件的,则输出 Orz, I cannot find x!,注意逗号与 I 之间有一个空格。
样例输入 Copy
【样例输入1】 3 1 2 1 3 2 2 3 2 3 3 【样例输入2】 3 2 2 1 3 2 2 3 2 3 3样例输出 Copy
【样例输出1】 2 1 2 【样例输出2】 2 1 0提示
数据范围与提示
对于全部数据,1≤y,z,p≤10^9,1≤T≤10,且保证 p 为质数。
以下为同余理论常见符号
任务一可通过快速幂的板子解决
ll fpow(ll a,ll b,ll k)
{
ll r=1;
while(b)
{
if(b&1) r=(r*a)%k;
b>>=1;
a=(a*a)%k;
}
return r;
}
任务二即线性同余方程,可运用扩展欧几里得算法(exgcd) ,其前置知识为裴蜀定理。
(此外还可以采用费马小定理 个人感觉此处采用费马小定理会更方便,展示见最后)
ll exgcd(ll a, ll b, ll& x, ll& y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
ll d = exgcd(b, a % b, x, y);
ll temp = x;
x = y;
y = temp - a / b * y;
return d;
}
因为exgcd求出来的是ax+by=gcd(a,b)的解,所以求出的解还需要进一步变形,即将解乘以c/gcd(a,b);
在该式中 x*y≡z (modp ) --->x*yk*p+z---->x*y+k*p=z。
任务三则运用了BSGS算法(大步小步)
BSGS(baby-step giant-step),即大步小步算法。常用于求解离散对数问题。该算法可在O()的时间内求解
,其中
。(详细原理见链接来自oi-wiki)
ll bsgs(ll a, ll b, ll n) {
a %= n;
b %= n;
if (a == 0) return b == 0 ? 1 : -1;
if (b == 1) return 0;
ll t = sqrt(n) + 1;
map<ll, ll> Hash;
Hash.clear();
for (ll i = 0; i < t; i++) {
ll val = b * fpow(a, i, n) % n;
Hash[val] = i;
}
a = fpow(a, t, n);
for (ll i = 1; i <= t; i++) {
ll now = fpow(a, i, n);
if (Hash.find(now) != Hash.end()) now = Hash[now];
else now = -1;
if (now != -1) return i * t - now;
}
return -1;
}
最终代码如下
#include<bits/stdc++.h>
#define INF 0x3f3f3f
#define endl "\n"
typedef long long ll;
using namespace std;
const ll N=2e5+10;
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
//快速幂
ll fpow(ll a,ll b,ll k)
{
ll r=1;
while(b)
{
if(b&1) r=(r*a)%k;
b>>=1;
a=(a*a)%k;
}
return r;
}
ll exgcd(ll a, ll b, ll& x, ll& y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
ll d = exgcd(b, a % b, x, y);
ll temp = x;
x = y;
y = temp - a / b * y;
return d;
}
ll bsgs(ll a, ll b, ll n) {
a %= n;
b %= n;
if (a == 0) return b == 0 ? 1 : -1;
if (b == 1) return 0;
ll t = sqrt(n) + 1;
map<ll, ll> Hash;
Hash.clear();
for (ll i = 0; i < t; i++) {
ll val = b * fpow(a, i, n) % n;
Hash[val] = i;
}
a = fpow(a, t, n);
for (ll i = 1; i <= t; i++) {
ll now = fpow(a, i, n);
if (Hash.find(now) != Hash.end()) now = Hash[now];
else now = -1;
if (now != -1) return i * t - now;
}
return -1;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
ll t,k,a,b,x,ans,m,n;
ll p;
cin>>t>>k;
while(t--)
{
cin>>a>>b>>p;
if(k==1)
{
a%=p;
ans=fpow(a,b,p);
cout<<ans<<endl;
}
else if(k==2)
{
ll x,y;
ll d=exgcd(a,p,x,y);
if(b%d)
{
cout<<"Orz, I cannot find x!"<<endl;
continue;
}
ll t=p/d;
while(x<0) x+=t;
x=((x*b)%t+t)%t;
cout<<x<<endl;
}
/* else if(k==2)
{
y%=p;
z%=p;
x=fpow(y,p-2,p)*z%p;
if(!y&&z)
{
cout<<"Orz, I cannot find x!"<<endl;
continue;
}
else cout<<x<<endl;
} 费马小定理解法*/
else if(k==3)
{
ll ans=bsgs(a,b,p);
if(ans==-1) cout<<"Orz, I cannot find x!"<<endl;
else cout<<ans<<endl;
}
}
return 0;
}