[bzoj2242][exgcd][BSGS]计算器

109 篇文章 4 订阅
63 篇文章 0 订阅

Description

你被要求设计一个计算器完成以下三项任务:
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)的最小非负整数。

Input

输入包含多组数据。

第一行包含两个正整数T,K分别表示数据组数和询问类型(对于一个测试点内的所有数据,询问类型相同)。
以下行每行包含三个正整数y,z,p,描述一个询问。

Output

对于每个询问,输出一行答案。对于询问类型2和3,如果不存在满足条件的,则输出“Orz, I cannot find
x!”,注意逗号与“I”之间有一个空格。

Sample Input

【样例输入1】
3 1
2 1 3
2 2 3
2 3 3

【样例输入2】
3 2
2 1 3
2 2 3
2 3 3

【数据规模和约定】

对于100%的数据,1<=y,z,p<=10^9,为质数,1<=T<=10。

Sample Output

【样例输出1】
2
1
2

【样例输出2】
2
1
0

题解

这题其实就是来巩固数论知识的。。
第一问快速幂不会右转百度
第二问扩展欧几里得不会右转百度,观察题目发现p是质数于是还可以用欧拉
重点在第三问因为我没有学过高次同余方程。。这里我就口胡一下BSGS算法吧
还是因为p是质数,实质上给BSGS减了一些复杂度。根据费马小定理可以知道答案一定不超过p
因为不会打恒等号我就用等号代替了
设m为sqrt(p) 向上取整,那么次数可以表示为k*m+c。
原式可以化为y^k*m+c=z(mod p)
再化简得到(y^k*m) * (y^c)=z(mod p)
移项得y^k*m=z*(y^-c) (mod p)
即为y^k*m=z*(y^c的逆元) (mod p)
其中c<m,右半部分我们可以暴力出来他们的值,拿一个map记录一下
暴力完后枚举k,判一下map中有没有这个值,有就输出。。否则枚举完后输出无解
y^c的逆元可以用y^c-1的逆元递推求出
如此就是BSGS的思想啦,对于这题就足够了。那么p非质数的情况请大家右转百度

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<map>
using namespace std;
typedef long long LL;
LL exgcd(LL a,LL b,LL &x,LL &y)
{
    if(a==0)
    {
        x=0;y=1;return b;
    }
    else
    {
        LL tx,ty;
        LL d=exgcd(b%a,a,tx,ty);
        x=ty-(b/a)*tx;
        y=tx;
        return d;
    }
}
int pow_mod(int a,int b,int mod)
{
    int ans=1%mod;a%=mod;
    while(b)
    {
        if(b%2==1)ans=(LL)ans*a%mod;
        a=(LL)a*a%mod;b/=2;
    }
    return ans;
}
map<int,int> mp;
void BSGS(int y,int z,int mod)
{
    if(y==0&&z==0){printf("1\n");return;}  
    if(y==0&&z!=0){printf("Orz, I cannot find x!\n");return;}
    mp.clear();
    int k=ceil(sqrt(mod)),tmp=1,p=pow_mod(y,mod-2,mod);
    mp[z]=k+1;
    for(int i=1;i<k;i++)
    {
        tmp=(LL)tmp*p%mod;
        int t=(LL)tmp*z%mod;
        if(mp[t]==0)mp[t]=i;
    }
    tmp=1;p=pow_mod(y,k,mod);
    for(int i=0;i<k;i++)
    {
        if(mp[tmp])
        {
            if(mp[tmp]==k+1)printf("%d\n",i*k);
            else printf("%d\n",i*k+mp[tmp]);
            return ;
        }
        tmp=(LL)tmp*p%mod;
    }
    printf("Orz, I cannot find x!\n");
    return ;
}
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        int p,q,op;
        scanf("%d%d%d",&p,&q,&op);
        p%=op;
        if(k==1)printf("%d\n",pow_mod(p,q,op));
        else if(k==2)
        {
            LL a=p,b=op,K=q,x,y;
            LL d=exgcd(a,b,x,y);
            if(k%d!=0){printf("Orz, I cannot find x!\n");continue;}
            x=((x*(K/d))%(b/d)+(b/d))%(b/d);
            printf("%lld\n",x);
        }
        else q%=op,BSGS(p,q,op);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值