BZOJ2242 [SDOI2011]计算器 题解&代码

题意:有三种要求:
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)的最小非负整数。
分别处理即可

思路:
1显然是快速幂了,纯模板
2是扩展欧几里得(exgcd),求满足xy-pk=z的最小x(k任意)
3利用了费马小定理的性质a^(p-1)≡1(mod p),然后分块降复杂度(太麻烦懒得写直接抄黄学长代码…捂脸)

/**************************************************************
    Problem: 2242
    User: Rainbow6174
    Language: C++
    Result: Accepted
    Time:2028 ms
    Memory:3272 kb
****************************************************************/

#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
#define LL long long
using namespace std;
int T,k;
map<int,int> vis;
LL y,z,x,a,b,m,t,mod,temp,flag;
LL pow(LL base,LL x)
{
    LL ret = 1;
    base %= mod;
    while(x)
    {
        if(x & 1)ret *= base,ret %= mod;
        base *= base;
        base %= mod;
        x >>= 1;
    }
    return ret;
}
LL gcd(LL x,LL y)
{
    if(!y)return x;
    return gcd(y,x%y);
}
LL exgcd(LL x,LL y)
{
    if(!y)
    {
        a=1,b=0;
        return x;
    }
    int ret=exgcd(y,x%y);
    //cout<<a<<' '<<b<<' '<<x<<' '<<y<<endl;
    temp=a;
    a=b;
    b=temp-(x/y)*b;
    return ret;
}
int main(void)
{
    //freopen("calc.in","r",stdin);
    //freopen("calc.out","w",stdout);
    scanf("%d%d",&T,&k);
    while(T--)
    {
        scanf("%lld%lld%lld",&y,&z,&mod);
        if(k==1)printf("%lld\n",pow(y,z));
        y%=mod;
        z%=mod;
        if(k==2)
        {
            if(!z)
            {
                printf("0\n");
                continue;
            }
            if(!y || gcd(y,mod)>z)
            {
                printf("Orz, I cannot find x!\n");
                continue;
            }
            //cout<<y<<' '<<mod<<endl;
            exgcd(y,mod);
            a+=mod;
            a%=mod;
            a*=z/gcd(y,mod);
            a%=mod;
            printf("%lld\n",a);
        }
        if(k==3)
        {
            flag=0;
            if(!y)
            {
                if(!z)puts("1");
                else puts("Orz, I cannot find x!");
                continue;
            }
            vis.clear();
            LL a=ceil(sqrt(mod)),t=1;
            vis[1]=a+1;
            for(LL i=1;i<a;i++)
            {
                t=t*y%mod;
                if(!vis[t])vis[t]=i;
            }
            temp=pow(y,mod-a-1),b=1;
            for(LL i=0;i<a;i++)
            {
                int temp1=vis[z*b%mod];
                if(temp1)
                {
                    if(temp1==a+1)temp1=0;
                    printf("%lld\n",i*a+temp1);
                    flag=1;
                    break;
                }
                b=b*temp%mod;
            }
            if(!flag)puts("Orz, I cannot find x!");
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值