大步小步走算法处理这样的问题:

A^x = B (mod C)

求满足条件的最小的x(可能无解)

其中,A/B/C都可以是很大的数(long long以内)

先分类考虑一下:

当(A,C)==1 即A、C互质的时候,

叫他BSGS:

A一定存在mod C意义下的逆元,所以,A^k也存在。

注意到,A^(fai(c)) = 1 (mod c)  ......................(fai(c)表示c的欧拉函数值)

所以,A^(fai(c)+1) = A (mod C) 就重新回去了。

所以,最小的x值,如果有解,必然小于等于fai(c)

枚举显然是不可靠的。

这样考虑:将对数值分解:x=x1+x2;

将A^(0~sqrt(fai(c)) mod C 算出来,加入到一个hash表中,

这样,A^x=A^(x1+x2) 让 x1 取成:0*sqrt(fai(c) ,1*(sqrt(fai(c)) ,2*(sqrt(fai(c)) ... fai(c)

A^(p*sqrt(fai(c))+x2) = B mod C

因为,A^k都存在逆元,所以可以直接把A^(sqrt(fai(c))逆元预处理出来,再在每次循环p的时候,把A^(p*sqrt(fai(c))除过去;

即:A^x2 = B*ni mod C

对于这个B*ni(取模后),只需要在之前处理的hash表中查一下有没有出现就可以、

出现了就对应一个x2,对于x ,就是p*sqrt(fai(c))+x2

否则继续循环p

为了保证这个x是最小的x,

我们在建hash表的时候,是x的值从小到大建,如果这个值之前没有出现,就插入,否则不进行操作(相当于用小的x覆盖大的)

②我们分块的时候,从小到大枚举p,所以找到的第一个就是答案。

如果一直没有找到,就返回无解。

复杂度:O(sqrt(c)) (哈希表用邻接表挂链实现,不用map的log复杂度)

BSGS代码:poj2417(这个保证了模数是质数(直接用的费马),但是其实不一定是)

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cmath>
  4. #include<iostream>
  5. #include<map>
  6. #include<cstring>
  7. using namespace std;
  8. typedef long long ll;
  9. const int N=;
  10. const int mod=;
  11. ll p,A,B;
  12. ll ni[N];
  13. struct node{
  14. int nxt[mod],val[mod],id[mod],cnt;
  15. int hd[mod];
  16. void init(){
  17. memset(nxt,,sizeof nxt),memset(val,,sizeof val);cnt=;
  18. memset(hd,,sizeof hd);memset(id,,sizeof id);
  19. }
  20. void insert(ll x,int d){
  21. int st=x%mod;
  22. for(int i=hd[st];i;i=nxt[i]){
  23. if(val[i]==x) return;
  24. }
  25. val[++cnt]=x;nxt[cnt]=hd[st];id[cnt]=d;hd[st]=cnt;
  26. }
  27. int find(ll x){
  28. int st=x%mod;
  29. for(int i=hd[st];i;i=nxt[i]){
  30. if(val[i]==x) return id[i];
  31. }
  32. return -;
  33. }
  34. }ha;
  35. map<ll,int>mp;
  36. ll qm(ll a,ll b){
  37. ll ret=,base=a;
  38. while(b){
  39. if(b&) ret=(ret*base)%p;
  40. base=(base*base)%p;
  41. b>>=;
  42. }
  43. return ret;
  44. }
  45. ll BSGS(){
  46. ll up=(ll)floor(sqrt(1.0*p-))+;
  47. cout<<up<<endl;
  48. ni[]=;
  49. for(int i=;i<=up;i++){
  50. ni[i]=qm(qm(A,i*up),p-);
  51. }
  52. for(int i=;i<up;i++){
  53. ll t=qm(A,i);
  54. ha.insert(t,i);
  55. }
  56. for(int i=;i<=up;i++){
  57. if(i*up>p-) break;
  58. ll ri=B*ni[i]%p;
  59. ll ret=ha.find(ri);
  60. if(ret>=) return i*up+ret;
  61. }
  62. return -;
  63. }
  64. int main()
  65. {
  66. while(scanf("%lld",&p)!=EOF){
  67. scanf("%lld%lld",&A,&B);
  68. ha.init();
  69. ll ret=BSGS();
  70. if(ret==-){
  71. puts("no solution");
  72. }
  73. else{
  74. printf("%lld\n",ret);
  75. }
  76. }
  77. return ;
  78. }

BSGS

EXBSGS:

如果(A,C)!=1怎么办?

转化成互质的!!

设g=gcd(A,C)
A^x = B mod C

如果B不能被g整除,就break掉;(后面已经没意义了)

否则同除以g A^(x-1) * A/g = B/g mod C/g

这个是等价的变形。

注意到,A/g C/g 是互质的。

设g=gcd(A, C/g)

循环处理。。。。。

直到g == 1结束。

设进行了num次,现在得到的等式是:

A^(x-num) * A/πg = B/πg mod C/πg

现在,A和C/πg是互质的了。

A/πg也和C/πg互质,所以直接转化成逆元,乘过去。

形式是这样的:

A^x = NB mod C

其中(A,C)=1可以用BSGS了。

注意:这里求出来的是x>=num 的最小解

我们还要暴力枚举一发x = 0~num

直接通过原式子验证。

因为num一定是log级别的,所以不费事。

EXBSGS代码:poj3243

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstdlib>
  4. #include<iostream>
  5. #include<cstring>
  6. #include<cmath>
  7. using namespace std;
  8. typedef long long ll;
  9. const int N=;//sqrt fai()
  10. const int mod=;
  11. ll C,A,B;
  12. ll ni[N];
  13. struct node{
  14. int nxt[mod],val[mod],id[mod],cnt;
  15. int hd[mod];
  16. void init(){
  17. memset(nxt,,sizeof nxt),memset(val,,sizeof val),memset(id,,sizeof id);
  18. memset(hd,,sizeof hd),cnt=;
  19. }
  20. void insert(ll x,int d){
  21. int st=x%mod;
  22. for(int i=hd[st];i;i=nxt[i]){
  23. if(val[i]==x) return;
  24. }
  25. val[++cnt]=x,nxt[cnt]=hd[st],id[cnt]=d,hd[st]=cnt;
  26. }
  27. int find(ll x){
  28. int st=x%mod;
  29. for(int i=hd[st];i;i=nxt[i]){
  30. if(val[i]==x) return id[i];
  31. }
  32. return -;
  33. }
  34. }ha;
  35. void exgcd(ll a,ll b,ll &x,ll &y){
  36. if(b==){
  37. x=,y=;return;
  38. }
  39. exgcd(b,a%b,y,x);
  40. y-=(a/b)*x;
  41. }
  42. ll qm(ll a,ll b,ll p){
  43. ll ret=,base=a;
  44. while(b){
  45. if(b&) ret=(base*ret)%p;
  46. base=(base*base)%p;
  47. b>>=;
  48. }
  49. return ret;
  50. }
  51. int gcd(int a,int b){
  52. return (b==)?a:gcd(b,a%b);
  53. }
  54. int BSGS(ll a,ll b,ll c){
  55. int up=(int)floor(sqrt(1.0*c-))+;
  56. ll ni=,yy=;
  57. exgcd(qm(a,up,c),c,ni,yy);
  58. ni=(ni%c+c)%c;//warning!!! 必须变成正数
  59. ll kk=;
  60. for(int i=;i<=up-;i++){
  61. ha.insert(kk,i);
  62. kk=(kk*a)%c;
  63. }
  64. ll bb=b;
  65. for(int i=;i<=up;i++){
  66. int kk=ha.find(bb);
  67. if(kk>=) return i*up+kk;
  68. bb=(bb*ni)%c;//不断找逆元 递推就可以
  69. }
  70. return -;
  71. }
  72. int EXBSGS(){
  73. int num=;
  74. int yC=C;
  75. int yB=B;
  76. int yA=A;
  77. ll ji=;
  78. int ret=-;
  79. bool flag=false;
  80. while(){
  81. int g=gcd(A,C);
  82. if(g==) break;
  83. if(B%g) {
  84. flag=true;break;
  85. }
  86. B/=g,C/=g,ji=(ji*A/g)%C;
  87. num++;
  88. }
  89. for(int i=;i<=num;i++){
  90. ll kk=qm(yA,i,yC);
  91. if(kk%yC==yB) return i;
  92. }
  93. if(!flag){
  94. ll ni,yy;
  95. exgcd(ji,C,ni,yy);
  96. ni=(ni%C+C)%C;//warning!!! 必须变成正数
  97. ll NB=(B*ni)%C;
  98. ret=BSGS(A,NB,C);
  99. }
  100. if(ret>=) return ret+num;
  101. else return -;
  102. }
  103. int main(){
  104. while(){
  105. scanf("%lld%lld%lld",&A,&C,&B);
  106. if(A==&&B==&&C==) break;
  107. ha.init();
  108. B%=C;
  109. int ret=EXBSGS();
  110. if(ret>=){
  111. printf("%d\n",ret);
  112. }
  113. else{
  114. puts("No Solution");
  115. }
  116. }
  117. return ;
  118. }

EXBSGS

我的易错点:

①BSGS和EXBSGS中,总是忘了对B或者NB取模,就爆long long 了。(日常模一模)

②C不一定是质数,所以用exgcd求逆元(欧拉定理亲测也行,只要你不嫌sqrt麻烦)

③分块求每一块大小的时候,up=floor(sqrt(C))+1注意一定要加一,否则floor就卡下去了。对于C是质数,就可能不能取到C-1

比如:73^x = 71 mod 139 (139是质数)的解是:136

如果不加1,up=11 那么,最多能分块到:121+11=132 就输出无解了。

④用exgcd求逆元的时候,必须把求出来的逆元:ni=(ni%C+C)%C转化为正数!!!

BSGS&EXBSGS 大手拉小手,大步小步走的更多相关文章

  1. 离散对数&&大步小步算法及扩展

    bsgs algorithm ax≡b(mod n) 大步小步算法,这个算法有一定的局限性,只有当gcd(a,m)=1时才可以用 原理 此处讨论n为素数的时候. ax≡b(mod n)(n为素数) 由 ...

  2. BSGS && EXBSGS

    基础BSGS 用处是什么呢w 大步小步发(Baby-Step-Giant-Step,简称BSGS),可以用来高效求解形如\(A^x≡B(mod C)\)(C为素数)的同余方程. 常用于求解离散对数问题 ...

  3. [模板]大步小步算法——BSGS算法

    大步小步算法用于解决:已知A, B, C,求X使得 A^x = B (mod C) 成立. 我们令x = im - j | m = ceil(sqrt(C)), i = [1, m], j = [0, ...

  4. 离散对数及其拓展 大步小步算法 BSGS

    离散对数及其拓展 离散对数是在群Zp∗Z_{p}^{*}Zp∗​而言的,其中ppp是素数.即在在群Zp∗Z_{p}^{*}Zp∗​内,aaa是生成元,求关于xxx的方程ax=ba^x=bax=b的解, ...

  5. JQ方法实用案例///鼠标移动到div和修改ipt中弹窗、CSS鼠标变小手、JQ获取元素属性、JQ选择器

    今天学习了jQ,jQ对js的帮助很大,菜鸟教程上也有属性.可以查看 js 和 jquery主要的区别 在 dom    想用jquery  必须先引入(顺序问题)        先css 再js:   ...

  6. 【题解】Matrix BZOJ 4128 矩阵求逆 离散对数 大步小步算法

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4128 大水题一道 使用大步小步算法,把数字的运算换成矩阵的运算就好了 矩阵求逆?这么基础的线 ...

  7. [luogu4195 Spoj3105] Mod (大步小步)

    传送门 题目描述 已知数a,p,b,求满足a^x≡b(mod p)的最小自然数x. 输入输出格式 输入格式: 每个测试文件中最多包含100组测试数据. 每组数据中,每行包含3个正整数a,p,b. 当a ...

  8. (day67)组件、组件化、组件传参、JS补充(命名转换、for in 、数据转换)、css取消选中和模拟小手

    目录 一.初识组件 (一)概念 (二)特点 二.组件的分类 (一)根组件 (二)局部组件 (三)全局组件 二.数据组件化 三.组件的传参 (一)父传子 (二)子传父 四.JS补充 (一)与html命名 ...

  9. 数据结构:堆排序 (python版) 小顶堆实现从大到小排序 | 大顶堆实现从小到大排序

    #!/usr/bin/env python # -*- coding:utf-8 -*- ''' Author: Minion-Xu 小堆序实现从大到小排序,大堆序实现从小到大排序 重点的地方:小堆序 ...

随机推荐

  1. 将 C# 枚举序列化为 JSON 字符串 基础理论

    该转换过程需要引用 Newtonsoft.JSON,这其中的转换过程还是蛮有意思的. 一.定义枚举 /// <summary> /// 托寄物品枚举 /// </summary> ...

  2. 20135337——实践一:Linux基础配置

    一.配置系统,权限中简单梳理遇到的问题 1.Ubuntu中root和普通用户相互切换 1.从user用户切换到root用户 执行:sudo su 2.从root用户切回user用户 执行:su use ...

  3. SuperMaze(Hello World 团队)Alpha版使用说明

    一.产品介绍 超级迷宫是一款android的手机游戏,目前我们已经在PC 端成功实现大体功能,虽然虽然迷宫游戏不少但我们的游戏渐渐的会假如自己的特色功能,尽量吸引用户,通过游戏开发智力,通过游戏打发无 ...

  4. C学习随笔

    1)要经常复习,一些基础的知识点,学过的.讲过的实例,应多看一下,学习并掌握编程的语法.思路.实验中可看出,不少同学对以前知识没有掌握,对讲过的实例没有理解2)要经常实践,纸上得来终觉浅,绝知此事要躬 ...

  5. QT QProgressBar QProgressDialog 模态,位置设置,无边框,进度条样式

    一  关于模态设置 QProgressDialog可以设置模态(需要在new的时候传入parent),QProgressBar设置不好: 只有dialog可以设置模态,widget不能设置模态(QPr ...

  6. Spring事务银行转账示例

    https://www.imooc.com/video/9331 声明式事务 @Transactiona() 编程式事务 非模板式(不使用TransactionTemplate) http://cai ...

  7. Activiti的部署问题

    http://www.kafeitu.me/activiti/2012/03/22/workflow-activiti-action.html 既可以通过每次Spring应用程序启动时,执行部署命令. ...

  8. python中lambda表达式中自由变量的坑,因为for循环结束了 变量还保存着,详见关于for循环的随笔

    http://blog.csdn.net/u010949971/article/details/70045537

  9. codeforces624A

    Save Luke CodeForces - 624A Luke Skywalker got locked up in a rubbish shredder between two presses. ...

  10. ansible系列7-mysql_user模块

    添加mysql的用户和权限.密码 新增mysql用户zhang,设置登录密码zhang,给予权限zabbix.*:ALL ansible dba -m mysql_user -a 'login_hos ...