• BSGS


    BSGS

    前置芝士

    (Baby-Step-Giant-Step) 算法,即大步小布算法,缩写为 (BSGS)

    作用

    解决类似 (y^xequiv z(mod~p)),给定 (y,z,p>=1) 求解 (x) 的问题

    (普通的 (BSGS) 只能求解 (gcd(y,p)=1) 的情况)

    推导过程

    (x=a*m+b,m=lceil sqrt p ceil,ain[0,m),bin[0,m))

    (y^{a*m}equiv z*y^{-b}(mod~p))

    怎么求解?(当然可以逆元,主要是懒)

    为了方便,设 (x=a∗m−b) ,那么(y^{a*m}equiv z∗y^b(mod~p))

    枚举 (bin[0,m]) ,将 (z*y^b) 存入 (hash) 表(也可存入 (map) ,但是常数较大,没有 (hash) 跑的快)

    枚举 (ain[1,m]) ,从 (hash) 表中寻找第一个满足 (y^{a*m}equiv z∗y^b(mod p))

    此时 (x=a*m-b) 即为所求 (这里面 (m=sqrt p)

    (接下来是短暂的证明 (m) 为什么取 (sqrt p)

    (x) 值最大时 (a=m,b=0) ,为 (x=m*m=p) ,超过 (p) 怎么办?

    有一个公式 (a^{p-1}equiv 1(mod~p)) 因此 (a^kequivdfrac{a^k}{a^{(p-1)*w}}(mod~p))

    (k>p)时,可化为 (a^kequiv a^{(k~mod~p-1)}(mod~p))

    所以 (x>p) 也会被%到 (x<p) ,这时可以直接找之前的答案

    例题

    P2485 [SDOI2011]计算器

    题意

    健达计算器,三个操作,一次满足(不是

    思路

    第一种:快速幂即可。

    第二种:用拓展欧几里得算法即可。已知 (a,b,p) ,求 (x) 的最小值,使得 (a*xequiv b(mod~p))

    ​ 可转化为:(a*x+p*y=b) ,要求 (gcd(a,p)|b) ,否则无解。

    ​ (鄙人偷懒,用了另一种办法:(xy=z(mod~p)~~=>~~xequiv z*y^{-1}(mod~p))

    ​ 然后用乘法逆元求解即可)

    第三种: (BSGS) 即可。

    (记得特判 (y|p) 的情况)

    代码
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<map>
    #include<cmath>
    #include<vector>
    #include<set>
    #define ll long long
    #define re register
    
    using namespace std;
    const int HashMod=100007;
    
    inline int read(){
    	re int x=0,f=1;
    	re char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    	while(isdigit(ch)) {x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    int fpow(int a,int b,int mod)
    {
    	int s=1;
    	while(b){
    		if(b&1)
    			s=1ll*s*a%mod;
    			a=1ll*a*a%mod;
    			b>>=1;
    	}
    	return s;
    }
    
    namespace Task1
    {
    	void Solve(int y,int z,int p){
    		printf("%d
    ",fpow(y,z,p));}
    }
    
    namespace Task2
    {
    	void Solve(int y,int z,int p){
    		if(y%p==0&&z%p)
    			puts("Orz, I cannot find x!");
    		else
    			printf("%lld
    ",1ll*fpow(y,p-2,p)*z%p);//就是这里,因为写了快速幂,就顺手用了一下  
    	}
    }
    
    namespace Task3
    {
    	struct HashTable//hash表  
    	{
    		struct Line{ int u,v,next;}e[1000000];
    		int h[HashMod],cnt;
    		void add(int u,int v,int w)
    		{
    			e[++cnt]=(Line){w,v,h[u]};h[u]=cnt;
    		}
    		void Clear(){memset(h,0,sizeof(h));cnt=0;}
    		void Hash(int x,int k){
    			int s=x%HashMod;
    			add(s,k,x);
    		}
    		int query(int x){
    			int s=x%HashMod;
    			for(re int i=h[s];i;i=e[i].next)
    				if(e[i].u==x) return e[i].v;
    			return -1;
    		}
    	}Hash;
    	void Solve(int y,int z,int p){
    		if(y%p==0){
    			puts("Orz, I cannot find x!");
    			return ;
    		}
    		y%=p;z%=p;
    		if(z==1) {
    			puts("0");
    			return ;
    		}
    		int m=sqrt(p)+1;Hash.Clear();//注意 m 要向上取整 
    		for(re int i=0,t=z;i<m;i++,t=1ll*t*y%p) Hash.Hash(t,i);//枚举x=a*m-b中的b  
    		for(re int a=1,tt=fpow(y,m,p),t=tt;a<=m;a++,t=1ll*t*tt%p)//枚举x=a*m-b中的a  
    		{
    			int b=Hash.query(t);
    			if(b==-1) continue;
    			printf("%d
    ",a*m-b);
    			return ;
    		}
    		puts("Orz, I cannot find x!");
    	}
    }
    
    int main(){
    	int T=read(),K=read();
    	while(T--)
    	{
    		int y=read(),z=read(),p=read();
    		if(K==1) Task1::Solve(y,z,p);
    		if(K==2) Task2::Solve(y,z,p);
    		if(K==3) Task3::Solve(y,z,p);
    	}
    	return 0;
    }
    

    这个就自己做吧,比例题还简单)

    至于 (ex~BSGS) 就在下一篇博客了

  • 相关阅读:
    PostMan-NewMan运行参数
    shell脚本学习简单记录笔记
    android开发okhttp-4.9.1源码大致流程解读
    android开发获取键盘高度以及判断键盘是否显示(兼容分屏模式)
    Android开发The style on this component requires your app theme to be Theme.AppCompat (or a descendant)的解决方法
    Linux开发Ubuntu安装man手册
    Android开发源码解读四大组件源码解读简单梳理
    Android开发涉及到的AMS类和ActivityThread类源码解读
    Android开发为什么主线程可以一直运行而不会退出来
    前端CryptoJS加密、后端解密代码实现参考
  • 原文地址:https://www.cnblogs.com/jasony/p/13377315.html
Copyright © 2020-2023  润新知