BSGS&EXBSGS 大手拉小手,大步小步走
大步小步走算法处理这样的问题:
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(这个保证了模数是质数(直接用的费马),但是其实不一定是)
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<map>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=;
const int mod=;
ll p,A,B;
ll ni[N];
struct node{
int nxt[mod],val[mod],id[mod],cnt;
int hd[mod];
void init(){
memset(nxt,,sizeof nxt),memset(val,,sizeof val);cnt=;
memset(hd,,sizeof hd);memset(id,,sizeof id);
}
void insert(ll x,int d){
int st=x%mod;
for(int i=hd[st];i;i=nxt[i]){
if(val[i]==x) return;
}
val[++cnt]=x;nxt[cnt]=hd[st];id[cnt]=d;hd[st]=cnt;
}
int find(ll x){
int st=x%mod;
for(int i=hd[st];i;i=nxt[i]){
if(val[i]==x) return id[i];
}
return -;
}
}ha;
map<ll,int>mp;
ll qm(ll a,ll b){
ll ret=,base=a;
while(b){
if(b&) ret=(ret*base)%p;
base=(base*base)%p;
b>>=;
}
return ret;
}
ll BSGS(){
ll up=(ll)floor(sqrt(1.0*p-))+;
cout<<up<<endl;
ni[]=;
for(int i=;i<=up;i++){
ni[i]=qm(qm(A,i*up),p-);
}
for(int i=;i<up;i++){
ll t=qm(A,i);
ha.insert(t,i);
}
for(int i=;i<=up;i++){
if(i*up>p-) break;
ll ri=B*ni[i]%p;
ll ret=ha.find(ri);
if(ret>=) return i*up+ret;
}
return -;
}
int main()
{
while(scanf("%lld",&p)!=EOF){
scanf("%lld%lld",&A,&B);
ha.init();
ll ret=BSGS();
if(ret==-){
puts("no solution");
}
else{
printf("%lld\n",ret);
}
}
return ;
}
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
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=;//sqrt fai()
const int mod=;
ll C,A,B;
ll ni[N];
struct node{
int nxt[mod],val[mod],id[mod],cnt;
int hd[mod];
void init(){
memset(nxt,,sizeof nxt),memset(val,,sizeof val),memset(id,,sizeof id);
memset(hd,,sizeof hd),cnt=;
}
void insert(ll x,int d){
int st=x%mod;
for(int i=hd[st];i;i=nxt[i]){
if(val[i]==x) return;
}
val[++cnt]=x,nxt[cnt]=hd[st],id[cnt]=d,hd[st]=cnt;
}
int find(ll x){
int st=x%mod;
for(int i=hd[st];i;i=nxt[i]){
if(val[i]==x) return id[i];
}
return -;
}
}ha;
void exgcd(ll a,ll b,ll &x,ll &y){
if(b==){
x=,y=;return;
}
exgcd(b,a%b,y,x);
y-=(a/b)*x;
}
ll qm(ll a,ll b,ll p){
ll ret=,base=a;
while(b){
if(b&) ret=(base*ret)%p;
base=(base*base)%p;
b>>=;
}
return ret;
}
int gcd(int a,int b){
return (b==)?a:gcd(b,a%b);
}
int BSGS(ll a,ll b,ll c){
int up=(int)floor(sqrt(1.0*c-))+;
ll ni=,yy=;
exgcd(qm(a,up,c),c,ni,yy);
ni=(ni%c+c)%c;//warning!!! 必须变成正数
ll kk=;
for(int i=;i<=up-;i++){
ha.insert(kk,i);
kk=(kk*a)%c;
}
ll bb=b;
for(int i=;i<=up;i++){
int kk=ha.find(bb);
if(kk>=) return i*up+kk;
bb=(bb*ni)%c;//不断找逆元 递推就可以
}
return -;
}
int EXBSGS(){
int num=;
int yC=C;
int yB=B;
int yA=A;
ll ji=;
int ret=-;
bool flag=false;
while(){
int g=gcd(A,C);
if(g==) break;
if(B%g) {
flag=true;break;
}
B/=g,C/=g,ji=(ji*A/g)%C;
num++;
}
for(int i=;i<=num;i++){
ll kk=qm(yA,i,yC);
if(kk%yC==yB) return i;
}
if(!flag){
ll ni,yy;
exgcd(ji,C,ni,yy);
ni=(ni%C+C)%C;//warning!!! 必须变成正数
ll NB=(B*ni)%C;
ret=BSGS(A,NB,C);
}
if(ret>=) return ret+num;
else return -;
}
int main(){
while(){
scanf("%lld%lld%lld",&A,&C,&B);
if(A==&&B==&&C==) break;
ha.init();
B%=C;
int ret=EXBSGS();
if(ret>=){
printf("%d\n",ret);
}
else{
puts("No Solution");
}
}
return ;
}
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 大手拉小手,大步小步走的更多相关文章
- 离散对数&&大步小步算法及扩展
bsgs algorithm ax≡b(mod n) 大步小步算法,这个算法有一定的局限性,只有当gcd(a,m)=1时才可以用 原理 此处讨论n为素数的时候. ax≡b(mod n)(n为素数) 由 ...
- BSGS && EXBSGS
基础BSGS 用处是什么呢w 大步小步发(Baby-Step-Giant-Step,简称BSGS),可以用来高效求解形如\(A^x≡B(mod C)\)(C为素数)的同余方程. 常用于求解离散对数问题 ...
- [模板]大步小步算法——BSGS算法
大步小步算法用于解决:已知A, B, C,求X使得 A^x = B (mod C) 成立. 我们令x = im - j | m = ceil(sqrt(C)), i = [1, m], j = [0, ...
- 离散对数及其拓展 大步小步算法 BSGS
离散对数及其拓展 离散对数是在群Zp∗Z_{p}^{*}Zp∗而言的,其中ppp是素数.即在在群Zp∗Z_{p}^{*}Zp∗内,aaa是生成元,求关于xxx的方程ax=ba^x=bax=b的解, ...
- JQ方法实用案例///鼠标移动到div和修改ipt中弹窗、CSS鼠标变小手、JQ获取元素属性、JQ选择器
今天学习了jQ,jQ对js的帮助很大,菜鸟教程上也有属性.可以查看 js 和 jquery主要的区别 在 dom 想用jquery 必须先引入(顺序问题) 先css 再js: ...
- 【题解】Matrix BZOJ 4128 矩阵求逆 离散对数 大步小步算法
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4128 大水题一道 使用大步小步算法,把数字的运算换成矩阵的运算就好了 矩阵求逆?这么基础的线 ...
- [luogu4195 Spoj3105] Mod (大步小步)
传送门 题目描述 已知数a,p,b,求满足a^x≡b(mod p)的最小自然数x. 输入输出格式 输入格式: 每个测试文件中最多包含100组测试数据. 每组数据中,每行包含3个正整数a,p,b. 当a ...
- (day67)组件、组件化、组件传参、JS补充(命名转换、for in 、数据转换)、css取消选中和模拟小手
目录 一.初识组件 (一)概念 (二)特点 二.组件的分类 (一)根组件 (二)局部组件 (三)全局组件 二.数据组件化 三.组件的传参 (一)父传子 (二)子传父 四.JS补充 (一)与html命名 ...
- 数据结构:堆排序 (python版) 小顶堆实现从大到小排序 | 大顶堆实现从小到大排序
#!/usr/bin/env python # -*- coding:utf-8 -*- ''' Author: Minion-Xu 小堆序实现从大到小排序,大堆序实现从小到大排序 重点的地方:小堆序 ...
随机推荐
- “论 ofo 是如何影响今日头条发展的”
近段时间, ofo 小黄车押金难退的消息频频曝出.尽管 OFO 已经宣布押金只能在线上退还,但是线上退押金也难,因此很多的用户还是选择到 ofo 北京总部“要个说法”.记者昨天在现场发现,位于北京中关 ...
- RabbitMQ 优先级队列-为队列赋权
RabbitMQ 消息收发是按顺序收发,一般情况下是先收到的消息先处理,即可以实现先进先出的消息处理.但如果消息者宕机或其他原因,导致消息接收以后,未确认,那么消息会重新Requeue到队列中,就打破 ...
- ABPZero中的Name和SurName处理,以及EmailAddress解决方案(完美)。
使用ABPzero的朋友们都知道,User表中有Name和Surname两个字段,这两个字段对于国内的用户来说相当的不友好. 以及我们的一些系统中是不会涉及到EmailAddress字段.也就是说不会 ...
- MVC_防止HttpPost重复提交
重复提交的场景很常见,可能是当时服务器延迟的原因,如购物车物品叠加,重复提交多个订单.常见的解决方法是提交后把Button在客户端Js禁用,或是用Js禁止后退键等.在ASP.NET MVC 3 Web ...
- 分布式监控系统Zabbix-图形集中展示插件Graphtree安装笔记
Zabbix想要集中展示图像,唯一的选择是screen,后来zatree解决了screen的问题,但性能不够好.Graphtree 由OneOaaS开发并开源出来,用来解决Zabbix的图形展示问题, ...
- Linux内核及分析 第一周 计算机是如何工作的?
C语言代码: int g(int x) { return x + 5; } int f(int x) { return g(x); } int main(void) { return f(5) + 1 ...
- 《Linux内核设计与实现》 第八周读书笔记 第四章 进程调度
20135307 张嘉琪 第八周读书笔记 第四章 进程调度 调度程序负责决定将哪个进程投入运行,何时运行以及运行多长时间,进程调度程序可看做在可运行态进程之间分配有限的处理器时间资源的内核子系统.只有 ...
- Linux内核期末总结
20135316王剑桥<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC 1000029000 第一周 http://www.cn ...
- 变更RHEL(Red Hat Enterprise Linux 5.8)更新源使之自动更新
HP 4411s Install Red Hat Enterprise Linux 5.8) pick up from http://blog.chinaunix.net/uid-423637-id- ...
- [转帖]Nginx的超时keeplive_timeout配置详解
Nginx的超时keeplive_timeout配置详解 https://blog.csdn.net/weixin_42350212/article/details/81123932 Nginx ...