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

211 篇文章 0 订阅
11 篇文章 0 订阅

题目描述

传送门
题目描述有问题! y,z,p 中只有 p 为质数!
题目描述有问题!y,z,p中只有 p 为质数!
题目描述有问题!y,z,p中只有 p 为质数!
重要的事情说三遍!

题解

对于操作1:
快速幂而已,不存在无解的情况。
对于操作2:
扩展欧几里得算法。有解的条件为:gcd(y,p)|z
证明:
已知

xyz(modp)

原式可化为:
xyap=z

问题可以转化为求是否有一组数x,a使 xyap=z
而如果把所有x,a的取值以及对应的 xyap 的得数建系的话,会发现最小的数为x,a的gcd,而其他所有的数都为gcd的倍数。
这样我们就可以得出:有解的条件为: gcd(y,p)|z

我们知道当 (y,p)=1 时我们可以利用扩展欧几里得求出 xyap=gcd(y,p) xyap=1 的解 x,a
所以将原式转换一下:

xzy+azp=1

xz=xaz=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;
                }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值