[Luogu P2485] [BZOJ 2242 ] [SDOI2011]计算器

3 篇文章 0 订阅
洛谷传送门
BZOJ传送门

题目描述

你被要求设计一个计算器完成以下三项任务:

1、给定 yzp y 、 z 、 p ,计算 yz mod p y z   m o d   p 的值;

2、给定 yzp y 、 z 、 p ,计算满足 xyz(mod p) x y ≡ z ( m o d   p ) 的最小非负整数x;

3、给定 yzp y 、 z 、 p ,计算满足 yxz(mod p) y x ≡ z ( m o d   p ) 的最小非负整数x。

为了拿到奖品,全力以赴吧!

输入输出格式

输入格式:

输入文件calc.in 包含多组数据。

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

以下T 行每行包含三个正整数 yzp y 、 z 、 p ,描述一个询问。

输出格式:

输出文件calc.out 包括 T T 行.

对于每个询问,输出一行答案。

对于询问类型 2 3 3 ,如果不存在满足条件的,则输出Orz, I cannot find x!

输入输出样例

输入样例#1:

3 1
2 1 3
2 2 3
2 3 3

输出样例#1:

2
1
2

输入样例#2:

3 2
2 1 3
2 2 3
2 3 3

输出样例#2:

2
1
0

输入样例#3:

4 3
2 1 3
2 2 3
2 3 3
2 4 3

输出样例#3:

0
1
Orz, I cannot find x!
0

说明

img

解题分析

对于第一个操作, 直接快速幂就可以了。

第二操作我们可以用exgcd直接解方程解决。

第三个操作我们就要用 BSGS B S G S (北上广深)解决了。

其实这玩意就是个分块优化的暴力, 设 seg=P s e g = P , 因为 Aϕ(P)1(mod P) A ϕ ( P ) ≡ 1 ( m o d   P ) , 所以 x<P x < P , 那么就可以写成 Ak×seg+mB(mod P) A k × s e g + m ≡ B ( m o d   P ) , 我们就可以先预处理 Am(m[0,seg) A m ( m ∈ [ 0 , s e g ) ,放在 map m a p 中, 再算出 Aseg×i A s e g × i , 用 exgcd e x g c d 解出对应的 Am A m , 查表即可。

总复杂度 O(MODlog(MOD)) O ( M O D l o g ( M O D ) ) 。 当然如果手写 hashmap h a s h m a p , 把 Am A m 移项可以做到 O(MOD) O ( M O D )

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <map>
#define R register
#define IN inline
#define W while
#define gc getchar()
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
std::map <int, int> mp;
IN int fpow(R int x, R int tim, R int mod)
{
    int ret = 1;
    W (tim)
    {
        if(tim & 1) ret = 1ll * ret * x % mod;
        x = 1ll * x * x % mod, tim >>= 1;
    }
    return ret;
}
int exgcd(R int a, R int b, int &x, int &y)
{
    if(!b) return x = 1, y = 0, a;
    int ret = exgcd(b, a % b, x, y);
    int buf = x; x = y, y = buf - 1ll * a / b * y;
    return ret;
}
int BSGS(R int A, R int B, R int MOD)
{
    mp.clear();
    if(MOD == 1) if(!B) return 0; else return -1;//几种特判
    if(B == 1) if(A) return 0; else return -1;
    if(!(A % MOD)) if(!B) return 1; else return -1;
    int bd = std::ceil(std::sqrt(MOD));
    int seg = 1, now = 1;
    for (R int i = 0; i < bd; ++i)
    {
        if(!mp.count(seg)) mp[seg] = i;//可能mp[seg]=0, 用count来验证
        seg = 1ll * seg * A % MOD;
    }
    int gcd, x, y;
    for (R int i = 0; i < bd; ++i)
    {
        gcd = exgcd(now, MOD, x, y);
        x = (1ll * x * B % MOD + MOD) % MOD;
        if(mp.count(x)) return i * bd + mp[x];//
        now = 1ll * now * seg % MOD;
    }
    return -1;
}
int main(void)
{
    int a, b, c;
    int T, typ, gcd, tim, ans, x, y;
    in(T), in(typ);
    W (T--)
    {
        in(a), in(b), in(c);
        if(typ == 1) printf("%d\n", fpow(a, b, c));
        else if(typ == 2)
        {
            gcd = exgcd(a, c, x, y);
            if(b % gcd) {puts("Orz, I cannot find x!"); continue;}
            tim = b / gcd; c /= gcd;
            printf("%lld\n", (1ll * x * tim % c + c) % c);
        }
        else
        {
            ans = BSGS(a, b, c);
            if(ans < 0) puts("Orz, I cannot find x!");
            else printf("%d\n", ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值