扩展大步小步--[SPOJ]3105

这题也是神坑之一···
细节很多···
没开long long 调了我一个小时气死爸爸哦

既然这样先来说一下大步小步
大步小步算法是专门用来求解a^x = b(mod n)这种方程的,这种问题也称为离散对数问题。
这个在之前gzz讲数学的笔记里有详细提到
A^x=B (mod C) (A,C)=1
知A,B,C 求最小非负x
设X=x1+x2
则a^x1*a^x2=b mod c
O(sqrt(phi(c))) 分成sqrt(phi(C))段
哈希建表表示A^x->x的映射,将0~sqrt(phi(c))放进去
X=k*sqrt(phi(c))+x2
A^x2=B*1/a^phi(c),在哈希表里找到对应的x2
这里求个逆元就好啦
枚举k*sqrt(phi(c))求x2
如果在根号phi(c)内有更小的循环节,则求出来的值要最小的

这个必须在a,c互质的情况下
那么不互质的情况
就是扩展大步小步
其实这个跟大步小步没有多大关系···
既然不互质就有gcd
那么我们不断把gcd提出来
直到a,p互质
再用大步小步思想求出
并且记录提出gcd的次数num
在0-num暴力算下有没有符合的情况

嗯注释给的很明晰了:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#define int long long
using namespace std;
int a,b,p;
map<int,int> mp;

inline int rd(){
  int x=0,f=1; char c=' ';
  while(c<'0' || c>'9') {if(c=='-') f=-1;c=getchar();}
  while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
  return x*f;
}

int gcd(int x,int y){
  if(y==0) return x;
  return gcd(y,x%y);
}

int exgcd(int a,int b,int &x,int &y){
  if(b==0) {x=1,y=0; return a;}
  int d=exgcd(b,a%b,x,y);
  int z=x;
  x=y;
  y=z-a/b*y;//这个地方写成了y*a/b调了一小时···千万别这样写啊
  return d;
}

int inv(int a,int b){
  int x,y;
  exgcd(a,b,x,y);
  return (x%b+b)%b;
}

int qpow(int x,int k,int mod){
  int ret=1;
  while(k){
    if(k&1) ret=(ret*x)%mod;
    x=(x*x)%mod;
    k>>=1;
  }
  return ret;
}

int phi(int n){
  int res=n;
  int t=sqrt(n);
  for(int i=2;i<=t;i++){
    if(n%i==0){
      res=(res/i)*(i-1);
      while(n%i==0) n/=i;
    }
  }
  if(n>1) res=(res/n)*(n-1);
  return res;
}

int solve(){
  int d;
  int aa=a,bb=b,pp=p;
  int x=1,num=0;
  while((d=gcd(a,p))!=1 && b%d==0){//同除gcd,但a不能直接除,用x记录除去的部分
    x*=a/d; b/=d; p/=d;
    num++; x%=p;
  }
  b=b*inv(x,p)%p;
  if(b==1) return num;
  if(b%gcd(a,p)) return -1;//如果b不能整除gcd(a,p)则无解
  int last=1;
  for(int i=0;i<=num;(last*=a)%=pp,i++){
    if(last==bb) return i;
  }//暴力枚举0~num之间是否存在k,存在则为最小的
  last=1;
  int now=phi(p);
  int bl=sqrt(now);
  for(int i=0;i<=bl;(last*=a)%=p,i++){//哈希建表
    int q=last;
    if(!mp.count(q)) mp[q]=i;
  }
  last=1;
  int k=qpow(a,bl,p);
  for(int i=0;i<=bl;(last*=k)%=p,i++){
    int q=b*inv(last,p)%p;//同常规BSGS一样
    if(mp.count(q))
      return i*bl+num+mp[q];
  }
  return -1;
}

signed main(){
  while(1){
    a=rd(); p=rd(); b=rd();
    if(a==0 && p==0 && b==0) break;
    b%=p;
    int k=solve();
    if(k==-1) printf("No Solution\n");
    else printf("%d\n",k);
    mp.clear();//记得清空map
  }
}

猜你喜欢

转载自blog.csdn.net/sizeof_you/article/details/80918879
今日推荐