BZOJ 2242: [SDOI2011]计算器

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

分析

第一问快速幂
第二问exgcd
第三问BSGS,好麻烦我就先略
为啥叫baby step giant step,我其实不是很懂
卓神说这是meet in the middle的一种运用
yx=z(modp) x=km+i
ykmyiz
yizine(ykm) (逆元)
用费马小定理显然可得 ine(ym)yp1m
设其为 ine(ykm)ine(y(k1)m)T
yi(0<=i<=m)
放入hash或者map
然后枚举k,查询 zine(ykm)
显然m取 p
复杂度比较优秀

代码

#include <bits/stdc++.h>

#define INF 0x7fffffff
#define ll long long

int T,K;

int read()
{
    int x = 0,k = 1;
    char ch= getchar();
    while (ch<'0' || ch > '9')
    {
        ch = getchar();
        if (ch == '-')
            k = -1;
    }
    while (ch >= '0' && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * k;
}

int gcd(int a,int b)
{
    return b == 0 ? a : gcd(b,a % b);
}

void exgcd(int a,int b,int &x,int &y)
{
    if (b == 0)
    {
        x = 1;
        y = 0;
        return ;
    }
    exgcd(b,a % b,x,y);
    int t = x;
    x = y;
    y = t - a / b * y;
}

int pow(ll y,int z,int p)
{
    y %= p;
    ll ans = 1;
    while (z)
    {
        if (z & 1)
            ans = ans * y % p;
        y = y * y % p;
        z >>= 1;
    }
    return ans;
}

void slove(int y,int z,int p)
{
    p = -p;
    int t = gcd(y,p);
    if (z % t)
    {
        printf("Orz, I cannot find x!\n");
        return;
    }
    y /= t;
    z /= t;
    p /= t;
    int a,b;
    exgcd(y,p,a,b);
    a = (ll)a * z % p;
    while (a < 0)
        a += p;
    printf("%d\n",a);
}

std::map<int,int> M;

void BSGS(int y,int z,int p)
{
    y %= p;
    if (!y && !z)
    {
        printf("1\n");
        return;
    }
    if (!y)
    {
        printf("Orz, I cannot find x!\n");
        return;
    }

    M.clear();
    ll m = ceil(sqrt(p)),t = 1;
    M[1] = m + 1;
    for (ll i = 1; i < m; i++)
    {
        t = t * y % p;
        if (!M[t])
            M[t] = i;
    }
    ll tmp = pow(y,p - m - 1,p);
    ll ine = 1;
    for (ll k = 0; k < m; k++)
    {
        int i = M[z * ine % p];
        if (i)
        {
            if (i == m + 1)
                i = 0;
            printf("%lld\n",k * m + i);
            return ;
        }
        ine = ine * tmp % p;
    }
    printf("Orz, I cannot find x!\n");
}

int main()
{
    T = read();
    K = read();
    while (T--)
    {
        int y = read(),z = read(),p = read();
        if (K == 1)
            printf("%d\n",pow(y,z,p));
        else if (K == 2)
            slove(y,z,p);
        else BSGS(y,z,p);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值