zoukankan      html  css  js  c++  java
  • BSGS 和扩展

    BSGS

    BSGS,全称叫 BabyStepGiantStep,也就是大步小步
    其实还是比较暴力的

    它可以(O(sqrt p))的复杂度内解出:

    [a^xequiv npmod p,gcd(a,p)=1 ]

    (x)的值
    如果(gcd(a,p) eq 1)就要用到 exBSGS 了

    我们考虑令(x=im-k,0le k<m)
    那么原式变为

    [a^{im-k}equiv npmod p ]

    [a^{im}equiv na^kpmod p ]

    那么,我肯可以枚举(kin[0,m)),计算出(na^kmod p)的值,然后把对应的(k)存下来
    可以用map或 hash,用map会多一个(log)

    然后,再枚举(iin[0,lceilfrac{p}{m} ceil]),计算出(a^{im}mod p)
    然后看一看之前有没有一个(k),使得(na^kequiv a^{im}pmod p),且(im>=k)
    如果有,那么答案肯定就是(im-k)
    如果一直没找到,那么就无解

    复杂度是(O(max(m,frac{p}{m} ))),显然,让(m=lceil sqrt p ceil)时最优,即为(O(sqrt p))

    但是,我们考虑的只是(xin[1,p])的情况,那为什么要找的这个(x)一定满足这样的性质呢?
    首先(gcd(a,p)=1),满足欧拉定理的条件,也就是会有(a^{varphi(p)}equiv 1pmod pRightarrow a^{qvarphi(p)}equiv 1pmod p,qinmathbb{N^*})
    那么,(kmod varphi(p)=k-qvarphi(p)<varphi(p)<p)
    (a^{kmod varphi(p)}=dfrac{a^k}{a^{qvarphi(p)}})
    又由于那个分母在(mod p)中同余于(1)
    所以(a^{kmod varphi(p)}equiv a^kpmod p)

    则可以得知,要求的最小的(x),如果有解,一定满足(x<varphi(p)<p)

    模板题:P3846 [TJOI2007] 可爱的质数
    这题保证了(a,n<p),所以不需要一些奇奇怪怪的特判

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<iomanip>
    #include<cstring>
    #include<map>
    #define reg register
    #define EN std::puts("")
    #define LL long long
    inline int read(){
    	register int x=0;register int y=1;
    	register char c=std::getchar();
    	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    	return y?x:-x;
    }
    std::map<int,int>map;
    inline int power(int a,int b,int p){
    	int ret=1;
    	while(b){
    		if(b&1) ret=1ll*ret*a%p;
    		b>>=1;a=1ll*a*a%p;
    	}
    	return ret;
    }
    inline int BSGS(int a,int n,int p){
    	reg int m=std::ceil(std::sqrt(p));
    	for(reg int i=0,s=n;i<m;i++,s=1ll*s*a%p) map[s]=i;
    	for(reg int i=0,tmp=power(a,m,p),s=1;i<=m;i++,s=1ll*s*tmp%p)
    		if(map.find(s)!=map.end())
    			if(i*m>=map[s]) return i*m-map[s];
    	return -1;
    }
    int main(){
    	int p=read(),a=read(),n=read();
    	LL ans=BSGS(a,n,p);
    	ans==-1?std::puts("no solution"):std::printf("%lld",ans);
    	return 0;
    }
    

    exBSGS

    用于处理上面哪个问题(gcd(a,p) eq 1)的情况
    有些细节稍显恶心(其实还好

    首先,原来那个同余方程可以等价的写成:

    [a^x+kp=n ]

    (gcd(a,p)=d)
    由于斐蜀定理,这个式子有解的充要条件是(dmid n),否则直接返回无解
    那么上面的式子就是:

    [a^{x-1}cdot frac{a}{d}+kcdotfrac{p}{d}=frac{n}{d} ]

    此时,(a^{x-1} ightarrow a^x,frac{p}{d} ightarrow p,frac{n}{d} ightarrow n)
    那么一直递归,直到(gcd(a,p)=1)

    所以此时设一共递归了(cnt)次,这所有次递归的(d)的乘积是(d)(用一个字母说起来省事
    原式就是

    [a^{x-cnt}cdot frac{a^{cnt}}{d}equiv frac{n}{d}pmod {frac{p}{d}} ]

    那么我们就用(a'=a,n'=frac{n}{d},p'=frac{p}{d})来跑一次 BSGS 就行了

    当然,答案还要再加上(cnt)
    而且,还有一个(dfrac{a^{cnt}}{d})的系数要乘上,具体对应代码中的变量ad,要传参到 BSGS 的函数里

    模板题:SP3105 MOD - Power Modulo InvertedP4195 【模板】exBSGS
    还是双倍经验,但是这个不保证(a,n<p),所以要先模一下,然后判断一下是不是等于(0),具体看代码
    而且这个还卡常,必须要用unordered_map,它的内部实现是哈希表,而map是红黑树,前者构建好像稍慢,但是查询快,适合这个题
    话说我写这题的时候听说卡常,然后T了以后猛卡常数,结果后来发现是写法错了,于是就对着错误的程序卡了一晚上常直至自闭
    不过是 c++11 的东西,noip可能不能用,编译时要加上-std=c++11

    当然写 hash 也可以,但我不太会/kk

    #include<cstdio>
    #include<map>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<iomanip>
    #include<cstring>
    #include<unordered_map>
    #define reg register
    #define EN std::puts("")
    #define LL long long
    inline int read(){
    	register int x=0;register int y=1;
    	register char c=std::getchar();
    	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    	return y?x:-x;
    }
    std::unordered_map<int,int>map;
    int gcd(int a,int b){return b?gcd(b,a%b):a;}
    inline int BSGS(int a,int n,int p,int ad){
    	map.clear();
    	reg int m=std::ceil(std::sqrt(p));
    	reg int s=1;
    	for(reg int i=0;i<m;i++,s=1ll*s*a%p) map[1ll*s*n%p]=i;
    	for(reg int i=0,tmp=s,s=ad;i<=m;i++,s=1ll*s*tmp%p)
    		if(map.find(s)!=map.end())
    			if(1ll*i*m-map[s]>=0) return 1ll*i*m-map[s];
    	return -1;
    }
    inline int exBSGS(int a,int n,int p){
    	a%=p;n%=p;
    	if(n==1||p==1) return 0;
    	reg int cnt=0;
    	reg int d,ad=1;
    	while((d=gcd(a,p))^1){
    		if(n%d) return -1;
    		cnt++;n/=d;p/=d;
    		ad=(1ll*ad*a/d)%p;
    		if(ad==n) return cnt;
    	}
    //		std::printf("a: %d n: %d p: %d ad:%d
    ",a,n,p,ad);
    	LL ans=BSGS(a,n,p,ad);
    	if(ans==-1) return -1;
    	return ans+cnt;
    } 
    signed main(){
    	int a=read(),p=read(),n=read();
    	while(a||p||n){
    		int ans=exBSGS(a,n,p);
    		if(~ans) std::printf("%d
    ",ans);
    		else std::puts("No Solution");
    		a=read();p=read();n=read();
    	}
    	return 0;
    }
    
  • 相关阅读:
    CSS箭头
    rails 路由正则表达式
    centos6.7 配置MongoDB日志
    centos6.7 配置Elasticsearch
    拼音纠错
    Pandas学习笔记
    WebService using Spring throwed org.xml.sax.SAXException: Bad envelope tag: htm
    怎样让Windows任务管理器CPU占用率呈现正玄曲线(解释+C#实现)
    Deepin Linux获得关注,国产GUI值得提倡
    Swing组件的另类嵌套
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/12666760.html
Copyright © 2011-2022 走看看