BSGS 算法,即 Baby Step,Giant Step 算法、拔山盖世算法。

计算 \(a^x \equiv b \pmod p\)。

\(p\)为质数时

特判掉 \(a,p\) 不互质的情况。

由于费马小定理 \(x^{p-1} \equiv 1 \pmod p\) 当 \(p\) 为质数,则要是暴力的话只需要枚举到 \(p-1\) 即可。

假设 \(x=it-j\),其中 \(t= \lceil \sqrt p \rceil,j \in [0,t]\),方程变为 \(a^{it-j} \equiv b \pmod p\),即 \(a^{it} \equiv ba^j \pmod p\)。我们惊喜地发现,左右最多也就 \(t\) 个左右种可能的取值(这就是 \(t\) 为什么取那个值的原因),那我们枚举 \(j\),把 \(ba^j\) 所对应的 \(j\) 都存起来,然后枚举 \(i\) 找有无对应即可。

保存的话用手写 hash 比 map 快很多的。

若 \(ba^j\) 冲突怎么办?答:存 \(j\) 大的。因为要想 \(x\) 尽量小,就要让 \(j\) 尽量大。

第一个找到的 \(i\) 和它对应的 \(j\) 就是答案。为什么?答:因为 \(i\) 变化 \(1\),\(x\) 变化的幅度是 \(t\)。

时间复杂度 \(\mathrm{O}(\sqrt{p})\)。

计算器:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
using namespace std;
typedef long long ll;
int T, k;
ll y, z, p;
map<int,int> d;
ll work1(ll a, ll b, ll p){
ll re=1;
while(b){
if(b&1) re = (re * a) % p;
a = (a * a) % p;
b >>= 1;
}
return re;
}
ll exgcd(ll a, ll b, ll &x, ll &y){
if(!b){
x = 1;
y = 0;
return a;
}
ll re=exgcd(b, a%b, x, y);
ll qwq=x;
x = y;
y = qwq - a / b * y;
return re;
}
void work2(){
ll u, v;
ll gcd=exgcd(y, p, u, v);
if(z%gcd) printf("Orz, I cannot find x!\n");
else
printf("%lld\n", ((u*z/gcd)%(p/gcd)+(p/gcd))%(p/gcd));
}
void work3(){
d.clear();
int m=sqrt(p);
if(m*m!=p) m++;
for(int i=0; i<=m; i++)
d[int((z*work1(y, i, p))%p)] = i;//当然这里也可以换掉快速幂,因为递推顺便就能求幂了
if(y%p==0){
if(z%p==1) printf("0\n");
else if(z%p==0) printf("1\n");
else printf("Orz, I cannot find x!\n");
return ;
}
for(int i=1; i<=m; i++){
int tmp=work1(y, i*m, p);
if(d.find(tmp)!=d.end()){
printf("%lld\n", (ll)i*m-d[tmp]);
return ;
}
}
printf("Orz, I cannot find x!\n");
}
int main(){
cin>>T>>k;
while(T--){
scanf("%lld %lld %lld", &y, &z, &p);
if(k==1) printf("%lld\n", work1(y, z, p));
if(k==2) work2();
if(k==3) work3();
}
return 0;
}

测试数据:

2 3
39 26 13
6 11 5
1
0

\(p\)无限制时

计算 \(a^x \equiv b \pmod p\),最难过的事情就是 \((a,p) \not = 1\)。那我们就想办法消掉一些 \(p\) 的因子让 \(a,p\) 互素。记 \(d=(a,p)\)。

倘若 \(d \nmid b\)(不整除的语法是 \nmid),则 \(b=1\) 时方程有解,解为 \(x=0\)。

倘若 \(d \mid b\) 且 \(d=1\),则就是普通的 bsgs。

否则,

\[a^x \equiv b \pmod p \Rightarrow \frac{a}{d} \times a^{x-1} \equiv \frac{b}{d} \pmod {\frac{p}{d}}
\]

当然,还有更难过的是 \((a,p/d)\) 可能还不为 \(1\)……那就继续搞下去,直到

\[\frac{a^k}{\prod_{i=1}^kd_i} \times a^{x-k} \equiv \frac{b}{\prod_{i=1}^kd_i} \pmod {\frac{p}{\prod_{i=1}^kd_i}}
\]

且 \((a,\frac{p}{\prod_{i=1}^kd_i})=1\)。

当然还有更更难过的是还没到互素的时候就有 \(\frac{p}{\prod_{i=1}^\kappa d_i} \nmid b\)……那唯一的可能就是 \(x=0\) 了。

然后发现如果真正的解 \(x<k\) 的话比较难过,那我们在累积公约数的时候可以顺便判断 \(x \in [0,k)\) 时是否是解。

对于 \(x \geq k\) 的情况,我们为了赏心悦目,把方程换元为

\[a' \times a^{x'} \equiv b' \pmod {p'}
\]

其中 \((a',p')=1\)。

这样就是一个普通的 bsgs。最终的解是 \(x=x'+k\)。

Clever Y:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
int a, b, p;
const int mod=32767;//这不是质数,不好
int gcd(int a, int b){
return !b?a:gcd(b,a%b);
}
int ksm(int a, int b, int p){
int re=1;
while(b){
if(b&1) re = ((ll)re * a) % p;
a = ((ll)a * a) % p;
b >>= 1;
}
return re;
}
struct HASHTABLE{
int nxt[200005], dai[200005], ret[200005], hea[200005], hmn;
void clear(){
memset(nxt, 0, sizeof(nxt));
memset(hea, 0, sizeof(hea));
hmn = 0;
}
void insert(int x, int y){
int tmp=x%mod;
for(int i=hea[tmp]; i; i=nxt[i])
if(dai[i]==x){
ret[i] = y;
return ;
}
nxt[++hmn] = hea[tmp];
dai[hmn] = x; ret[hmn] = y;
hea[tmp] = hmn;
}
int query(int x){
int tmp=x%mod;
for(int i=hea[tmp]; i; i=nxt[i])
if(dai[i]==x)
return ret[i];
return -1;
}
}hashTable;
int exbsgs(int a, int b, int p){
hashTable.clear();
a %= p; b %= p;
if(b==1) return 0;
int cnt=0, d=1, g;
do{
g = gcd(a, p);
if(b%g) return -1;
p /= g; b /= g;
d = ((ll)d * a / g) % p;
cnt++;
if(b==d) return cnt;
}while(g!=1);
int m=ceil(sqrt(p)), t=b;
for(int i=0; i<=m; i++){
hashTable.insert(t, i);
t = ((ll)t * a) % p;
}
g = ksm(a, m, p);
t = ((ll)d * g) % p;
for(int i=1; i<=m; i++){
int re=hashTable.query(t);
if(re>=0) return i*m-re+cnt;
t = ((ll)t * g) % p;
}
return -1;
}
int main(){
while(scanf("%d %d %d", &a, &p, &b)!=EOF){
if(a+b+p==0) break;
int re=exbsgs(a, b, p);
if(re==-1) printf("No Solution\n");
else printf("%d\n", re);
}
return 0;
}

luogu2485 [SDOI2011]计算器 poj3243 Clever Y BSGS算法的更多相关文章

  1. POJ 3243 Clever Y | BSGS算法完全版

    题目: 给你A,B,K 求最小的x满足Ax=B (mod K) 题解: 如果A,C互质请参考上一篇博客 将 Ax≡B(mod C) 看作是Ax+Cy=B方便叙述与处理. 我们将方程一直除去A,C的最大 ...

  2. poj3243 Clever Y[扩展BSGS]

    Clever Y Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 8666   Accepted: 2155 Descript ...

  3. BZOJ 2242 [SDOI2011]计算器 ——EXGCD/快速幂/BSGS

    三合一的题目. exgcd不解释,快速幂不解释. BSGS采用了一种不用写EXGCD的方法,写起来感觉好了很多. 比较坑,没给BSGS的样例(LAJI) #include <map> #i ...

  4. 【数论】【ex-BSGS】poj3243 Clever Y

    用于求解高次同余方程A^x≡B(mod C),其中C不一定是素数. http://blog.csdn.net/tsaid/article/details/7354716 这篇题解写得最好. 那啥,这题 ...

  5. bzoj2242: [SDOI2011]计算器 && BSGS 算法

    BSGS算法 给定y.z.p,计算满足yx mod p=z的最小非负整数x.p为质数(没法写数学公式,以下内容用心去感受吧) 设 x = i*m + j. 则 y^(j)≡z∗y^(-i*m)) (m ...

  6. BZOJ2242 [SDOI2011]计算器 【BSGS】

    2242: [SDOI2011]计算器 Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 4741  Solved: 1796 [Submit][Sta ...

  7. 【BZOJ2242】[SDOI2011]计算器 BSGS

    [BZOJ2242][SDOI2011]计算器 Description 你被要求设计一个计算器完成以下三项任务: 1.给定y,z,p,计算Y^Z Mod P 的值: 2.给定y,z,p,计算满足xy≡ ...

  8. bzoj 2242: [SDOI2011]计算器 BSGS+快速幂+扩展欧几里德

    2242: [SDOI2011]计算器 Time Limit: 10 Sec  Memory Limit: 512 MB[Submit][Status][Discuss] Description 你被 ...

  9. BZOJ 2242: [SDOI2011]计算器( 快速幂 + 扩展欧几里德 + BSGS )

    没什么好说的... --------------------------------------------------------------------- #include<cstdio&g ...

随机推荐

  1. _bzoj1191 [HNOI2006]超级英雄Hero【构图 并查集】

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1191 以锦囊作为节点,问题作为边“一步一步”构图,当一个时刻,某个联通块中边数>点数, ...

  2. 二分搜索 Codeforces Round #299 (Div. 2) C. Tavas and Karafs

    题目传送门 /* 题意:给定一个数列,求最大的r使得[l,r]的数字能在t次全变为0,每一次可以在m的长度内减1 二分搜索:搜索r,求出sum <= t * m的最大的r 详细解释:http:/ ...

  3. 寻找项目中顶级Vue对象 (一)

    个人博客首发博客园: http://www.cnblogs.com/zhangrunhao/ 参考 感谢作者 从一个奇怪的错误出发理解 Vue 基本概念 安装 - Vue.js 渲染函数 - Vue. ...

  4. 简单了解Linux中 du 和 df 以及它们的区别

    一 .du : 显示每个文件和目录的磁盘使用空间~~~文件的大小. 命令参数: -a   #显示目录中文件的大小  单位 KB . -b  #显示目录中文件的大小,以字节byte为单位. -c  #显 ...

  5. 用NPOI从Excel到DataTable

    NPOI功能强大,不用装Excel,就可以操作表格中数据----Excel.Sheet------>DataTable private IWorkbook workbook = null; pr ...

  6. 字符串、数组、json

    一.字符串 string 1.字符串的定义: (1).var s="haha"; (2).var s=new string ("hello") 对象形式定义 2 ...

  7. postgresql版sde(10.4.1)新建数据库

    使用sde pg版,第一次建sde库就是新安装sde的方法 然而从第二次开始可以有其他方式 新建一般的pg数据再转为sde数据库: 首先用建一般的pg数据库 首先要新建数据库 数据库名:新建的数据库名 ...

  8. Android单独继承View类来实现自定义控件

    一个单独继承view类来实现自定义控件,在该方法中,需要重写ondraw方法来绘制自己所需要的控件,下面也以一个简单的例子来说明如何实现自定义控件.该方法可以实现所需要的所有的自定义控件. 属性文件中 ...

  9. JSP 错误处理方法

    web.xml中配置error-page标签 1.WEB工程中打开 web.xml 文件

  10. IOStime处理

    对时间处理,在开发时,时常碰到.一般有获取具体的年月日和星期,两个不同时间的差,某一天的前一天或后一天等 .现在只介绍获取具体的年月日和星期,及某一天的前一天或后一天的方法: 对时间的处理一般都会用到 ...