bzoj 2242: [SDOI2011]计算器 BSGS算法学习

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

知识点:BSGS算法

据说这是meet in the middle的一种应用。
关于meet in the middle可以看这里Meet-in-the-middle思想的一些应用
这只是一种思想,顾名思义,就是通过转化让单向的问题变为双向然后相遇得到答案。
比如说图论里面的双向广搜,还有就是等式的变换。
通常可以缩小到 O(f(n)) O ( f ( n ) ) 的时间复杂度
bsgs就是用来解决 AxB(modP) A x ≡ B ( mod P ) 的问题的。
它有一个很中二的名字叫做拔山盖世算法。
做法是,设 x=km+r x = k ⋅ m + r
AkmArB(modP) A k m A r ≡ B ( mod P )
ArBinv(Akm)(modP) A r ≡ B ⋅ i n v ( A k m ) ( mod P )
由费马小定理可得
inv(Am)Apm1(modP) i n v ( A m ) ≡ A p − m − 1 ( mod P )
所以得到
inv(Akm)inv(A(k1)m)Apm1(modP) i n v ( A k m ) ≡ i n v ( A ( k − 1 ) m ) ⋅ A p − m − 1 ( mod P )
然后由MITM思想,把 AmodP A mod P 扔到Hash表里面,枚举k即可。
然后m取 P P 复杂度比较优秀

题目分析

第一问第二问快速幂,第三问BSGS即可。注意考虑无解的情况。

代码

/**************************************************************
    Problem: 2242
    User: 2014lvzelong
    Language: C++
    Result: Accepted
    Time:2016 ms
    Memory:3284 kb
****************************************************************/

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
using namespace std;
map<int, int>mp;
int read() {
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}
int pow(int x, int k, int p) {
    int b = 1;
    for(;k; x = 1LL * x * x % p, k >>= 1) if(k & 1) b = 1LL * b * x % p;
    return b;
}
void BSGS(int y, int z, int p) {
    if(!y && !z) {puts("1"); return;}
    if(!y) {puts("Orz, I cannot find x!"); return;}
    mp.clear();
    int m = ceil(sqrt(p)), t = 1;
    mp[1] = m + 1;
    for(int i = 1;i < m; ++i) {
        t = 1LL * t * y % p;
        if(!mp[t]) mp[t] = i;
    }
    int e = pow(y, p - m - 1, p), inv = 1;
    for(int i = 0;i < m; ++i, inv = 1LL * inv * e % p) {
        int r = mp[1LL * z * inv % p];
        if(r) {
            if(r == m + 1) r = 0;
            printf("%lld\n", 1LL * i * m + r);
            return ;
        }
    }
    puts("Orz, I cannot find x!");
}
int main() {
    int n = read(), t = read();
    while(n--) {
        int y = read(), z = read(), p = read(); y %= p;
        if(t == 1) printf("%d\n", pow(y, z, p));
        if(t == 2) {
            z %= p;
            if(!y && z) puts("Orz, I cannot find x!");
            else printf("%d\n", 1LL * z * pow(y, p - 2, p) % p);
        }
        if(t == 3) BSGS(y, z % p, p);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值