您的位置:首页 > 其它

[BZOJ2242][SDOI2011]计算器(快速幂+扩欧+BSGS)

2016-02-26 18:52 375 查看

题目描述

传送门

题目描述有问题!y,z,p中只有p为质数!

题目描述有问题!y,z,p中只有p为质数!

题目描述有问题!y,z,p中只有p为质数!

重要的事情说三遍!

题解

对于操作1:

快速幂而已,不存在无解的情况。

对于操作2:

扩展欧几里得算法。有解的条件为:gcd(y,p)|z

证明:

已知xy≡z(modp)

原式可化为:xy−ap=z

问题可以转化为求是否有一组数x,a使xy−ap=z

而如果把所有x,a的取值以及对应的xy−ap的得数建系的话,会发现最小的数为x,a的gcd,而其他所有的数都为gcd的倍数。

这样我们就可以得出:有解的条件为:gcd(y,p)|z

我们知道当(y,p)=1时我们可以利用扩展欧几里得求出xy−ap=gcd(y,p)即xy−ap=1的解x,a

所以将原式转换一下:

xzy+azp=1

令xz=x′,az=a′,求出x’和a’的值再与z相乘即为答案。

有人有问题吗?

能用扩展欧几里得求这个式子xzy+azp=1的解条件不是y,p互质吗?那要不要判断一下呢?

其实是不用的,因为如果满足这个式子gcd(y,p)|z的话,我们可以让等式两边同时/gcd(y,p),也就是y,z,p同时/gcd(y,p)。这样的话就保证了y,p一定互质啦。

对于操作3:

只是BSGS算法的模板啦,可以参考本博客BSGS算法学习笔记。

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
using namespace std;
#define LL long long

int T,type;
LL y,z,p,ans;
map <LL,LL> hash;

LL fast_pow(LL a,LL p,LL Mod)
{
LL ans=1;
for (;p;p>>=1LL,a=a*a%Mod)
if (p&1LL)
ans=ans*a%Mod;
return ans;
}
LL gcd(LL a,LL b)
{
if (!b) return a;
else return gcd(b,a%b);
}
void exgcd(LL a,LL b,LL &x,LL &y)
{
if (!b) x=1,y=0;
else exgcd(b,a%b,y,x),y-=a/b*x;
}
LL EXgcd(LL a,LL b,LL c)
{
LL t=gcd(a,b);
if (c%t) return -1;
a/=t,b/=t,c/=t;
LL x=0,y=0;
exgcd(a,b,x,y);
x=(x%b+b)%b;
if (!x) x+=b;
return (x*c%b+b)%b;
}
LL bsgs(LL a,LL b,LL p)
{
if (a%p==0) return -1;
hash.clear();
LL m=ceil(sqrt(p));
LL a_m=fast_pow(a,m,p);
LL mul=b;
for (LL j=0;j<=m;++j)
{
hash[mul]=j+1;
mul=mul*a%p;
}
mul=1;
for (LL i=1;i<=m;++i)
{
mul=mul*a_m%p;
if (hash[mul]) return i*m-(hash[mul]-1);
}
return -1;
}
int main()
{
scanf("%d%d",&T,&type);
while (T--)
{
scanf("%lld%lld%lld",&y,&z,&p);
switch(type)
{
case 1:
{
ans=fast_pow(y,z,p);
printf("%lld\n",ans);
break;
}
case 2:
{
ans=EXgcd(y,p,z);
if (ans==-1) puts("Orz, I cannot find x!");
else printf("%lld\n",ans);
break;
}
case 3:
{
ans=bsgs(y,z,p);
if (ans==-1) puts("Orz, I cannot find x!");
else printf("%lld\n",ans);
break;
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: