bzoj2242: [SDOI2011]计算器(数论)

传送门
数论基础题。
对于第一种情况用快速幂,第二种用 e x g c d exgcd exgcd,第三种用 b s g s bsgs bsgs


于是自己瞎 y y yy yy了一个 b s g s bsgs bsgs的板子(不知道是不是数据水了没卡如果有找出错的希望指正谢谢)
下面谈谈我对这个方法的理解。
实际上跟网上说的差不多。
要解 a x ≡ b m o d    p a^x\equiv b\mod p axbmodp
相当于令 p = k ∗ A + B , 0 ≤ B < p p=k*A+B,0\le B<p p=kA+B,0B<p
然后假设 x = k ′ ∗ A + B ′ , 0 ≤ B ′ < p x=k'*A+B',0\le B'<p x=kA+B,0B<p
那么 ( a A ) k ′ ≡ b ∗ ( a − 1 ) B ′ (a^A)^{k'}\equiv b*(a^-1)^{B'} (aA)kb(a1)B
于是枚举 k ′ k' k B ′ B' B即可,可以想到在 A A A s q r t p sqrt_p sqrtp的时候最坏复杂度最优。
代码:

#include
#include
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
typedef long long ll;
inline int ksm(int a,int p,int mod){int ret=1;for(;p;p>>=1,a=(ll)a*a%mod)if(p&1)ret=(ll)ret*a%mod;return ret;}
inline int gcd(int a,int b){while(b){int t=a;a=b,b=t%a;}return a;}
inline void exgcd(int a,int b,int&x,int&y){
	if(!b){x=1,y=0;return;}
	exgcd(b,a%b,x,y);
	int t=x;
	x=y,y=t-a/b*y;
}
inline int bsgs(int a,int b,int mod){
	a%=mod,b%=mod;
	if(!a&&!b)return 1;
	if(!a)return -1;
	tr1::unordered_map<int,int>S;
	int sqr=ceil(sqrt(mod-1)),inv=ksm(a,mod-2,mod);
	for(ri i=0,mul=b;i<sqr;++i,mul=(ll)mul*inv%mod)if(!S[mul])S[mul]=i?i:sqr;
	a=ksm(a,sqr,mod);
	for(ri i=0,mul=1;i*sqr<=mod;++i,mul=(ll)mul*a%mod)if(S[mul])return i*sqr+(S[mul])%sqr;
	return -1;
}
int main(){
	freopen("lx.in","r",stdin);
    for(ri a,b,p,tt=read(),k=read();tt;--tt){
        a=read(),b=read(),p=read();
        if(k==1){cout<<ksm(a%p,b,p)<<'\n';continue;}
        if(k==2){
            a%=p,b%=p;
            int g=gcd(a,p),x,y;
            if(b%g){puts("Orz, I cannot find x!");continue;}
            a/=g,b/=g,p/=g,exgcd(a,p,x,y),x=((ll)b*x%p+p)%p,cout<<x<<'\n';
            continue;
        }
        int tmp;
        if(~(tmp=bsgs(a,b,p)))cout<<tmp<<'\n';
        else puts("Orz, I cannot find x!");
    }
    return 0;
}

你可能感兴趣的:(#,数论)