BSGS&&EXBSGS——解决a^x%p=b问题

背景

对于关于x的a^x \equiv b \pmod p类型同余方程,求其最小解x_0

可以判断方程有无解

首先若p为素数,由完全剩余系可知有解,p不为素数:若b=0p|a,否则且对于b\neq 1,要若d|p,d|a=>d|b才可能有解。

若方程有解,由a^x \equiv a^{x \mod r} \pmod p,r为阶.可知将x0-p-1进行p次枚举一定可以得到解

BSGS就是将通过hash将枚举次数上界变为\sqrt p的算法


BSGS算法=>a^x \equiv b \pmod pgcd(a,p)=1

在上述的讲解中我们确定了枚举上界次数p,那么考虑如何将次数给降下来

对x做带余除法x=ix_0-j(0 \leq j <x_0),我们考虑对i进行枚举

对于每个i_0若我们能快速判断a^{i_0x_0-j}\equiv b \pmod p是否成立即可,变下形,即a^{i_0x_0} \equiv ba^j \pmod p

即每个a^{ix_0}值,判断是否有对应的ba^{j}值即可

可知若找到,则第一个值即为最小值(0 \leq j < x_0),若有多个j值,为了使x更小应取最大的j

对于ba^j值用hash表进行记录即可(若有相同的保证较大,用后者进行覆盖)

时间方面:hash处理为O(x_0),i的枚举为O(\lceil \frac{p}{x_0} \rceil),故取x_0=\lceil p \rceil合适

 

签到题

给定整数K和质数m,求最小的正整数N,使得 11\cdots1(N个1) \equiv K \pmod m≡K(modm)

2 \leq m \leq 10^{11},0 \leq K <m

 

11\cdots1看作等比数列\{10^0,10^1,\cdots 10^{N-1}\}的和,变形即为10^N \equiv 9K+1 \pmod m

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <unordered_map>
using namespace std;

using LL = long long;
const int MAXN = 1e6 + 5;
LL K, M, block, XN;
unordered_map<LL, int> MP;
LL qpow(LL, LL);
LL multi(LL a, LL b);

int main(){
  cin >> K >> M;
  LL tmp = 1; XN = 1;
  //while(tmp % M != K) tmp = (10 * tmp + 1) % M, XN++;
  //cout << XN << endl;
  K = (9 * K + 1LL) % M, block = (LL)ceil(sqrt(M));
  LL inv_K = qpow(K, M - 2);
  LL i, j;
  tmp = K, XN = 2e15;
  for(i = 0; i < block; i++){
    MP[tmp] = i;
    tmp = tmp * 10 % M;
  }
  for(i = 1; i <= block + 1; i++){
    tmp = qpow(10, i * block);
    if(MP.find(tmp) != MP.end()) XN = min(XN, i * block - MP[tmp]);
  }
  cout << XN << endl;
  return 0;
}

LL multi(LL a, LL b){
  return ((a * b - (LL)((LL)((long double)a / M * b + 1e-3) * M)) % M + M) % M;
}

LL qpow(LL x, LL n){
  LL res = 1;
  while(n){
    if(n & 1) res = multi(res, x);
    x = multi(x, x);
    n >>= 1;
  }
  return res;
}
//2
//1000000007
//696967963

EXBGSG算法:a^x \equiv b \pmod pgcd(a,p)>1

通过上述说明可以知道,我们可以在O(\sqrt p)时间对问题求解,但实际情况可能p \geq 10^{16},但gcd(a,p)>1

我们就需要对方程进行一定的预处理:

gcd(a,p)=d>1,可以知若d \nmid b要么b=1(直接可以得解)要么方程无解,若d | b,由同余运算可知

\frac{a^x}{d} \equiv \frac{b}{d} (mod\ \frac{p}{d})=>a^{x-1}\frac{a}{d} \equiv \frac{b}{d} (mod\ \frac{p}{d}),可以看到,相当于方程变为a^{x_0}*a_0\equiv b_0 (mod \ p_0)

一直进行该过程直到gcd(a,p_0)=1,方程变为a^{x-k}a_0\equiv b_0(mod\ p_0)进行枚举即可

过程中需要特判a_0 \equiv b_0(mod\ p_0)==>x=k的情况(后面枚举会有遗漏)

模板测试

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <unordered_map>
using namespace std;

using LL = long long;
LL A, B, P;
LL gcd(LL, LL);
LL qpow(LL, LL, LL);
LL exBSGS(LL, LL, LL);
LL getint();

int main(){
  ios::sync_with_stdio(false);
  int i;
  while(cin >> A >> P >> B){
    if(A != 0 && B != 0 && P != 0){
      LL ans = exBSGS(A, B, P);
      if(ans == -1) cout << "No Solution" << endl;
      else cout << ans << endl;
    }else break;
  }
  return 0;
}

unordered_map<LL, LL> MP;
LL exBSGS(LL a, LL b, LL p){
  a %= p, b %= p;
  if(!a) return (b) ? -1 : 0;
  if(b == 1) return 0;
  LL i, j, tmp, d = gcd(a, p), k = 0, t = 1;
  while(d != 1){
    if(b % d) return -1;
    k++;
    b /= d, p /= d, t = (t * (a / d)) % p;
    d = gcd(a, p);
    if(t == b) return k;
  }
  MP.clear();
  LL m = ceil(sqrt(p));
  for(j = 0, tmp = b; j < m; j++)
    MP[tmp] = j, tmp = tmp * a % p;
  j = qpow(a, m, p);
  for(i = 1, tmp = t; i <= m + 1; i++){
    tmp = tmp * j % p;
    if(MP[tmp]) return i * m - MP[tmp] + k;
  }
  return -1;
}

inline LL qpow(LL x, LL n, LL mod){
  LL res = 1;
  while(n){
    if(n & 1) res = res * x % mod;
    x = x * x % mod;
    n >>= 1;
  }
  return res;
}

inline LL gcd(LL x, LL y){
  return (!y) ? x : gcd(y, x % y);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值