#BSGS,原根#洛谷 11175 【模板】基于值域预处理的快速离散对数
题目
给定质数 \(P\) 以及它的一个原根 \(g\)。\(1\leq g<p\leq 10^9+7,q\leq 5\times 10^5\)
有 \(q\) 组询问,每组询问给出整数 \(y\),你需要找到最小的非负整数 \(x\) 使得 \(g^x\equiv y\pmod P\)。
分析
BSGS 显然能做到 \(O(q\sqrt P)\),但是会超时,那能不能通过预处理优化呢。
对于质数的原根来说,只要 \(x\) 小于 \(P\) 那它一定是最小的,那这个 \(x\) 实际上可以被视为对数。
那考虑预处理前 \(\sqrt P\) 个数的答案,那能不能拼凑呢,答案是显然的。
如果现在询问的 \(y\) 超出了根号,不妨假设 \(P=wy+z,z\) 是余数,那么 \(w\) 小于根号。
\(y=\frac{-1\cdot z}{w}\),同时取对数 \(\log(y)=\log(P-1)+\log(z)-\log(w)\)
并且 \(P=(w+1)y+z-y,y=\frac{y-z}{w+1}\),取对数得到 \(\log(y)=\log(y-z)-\log(w+1)\)
可以发现让 \(z\) 和 \(y-z\) 那个每次递归更小的那么就会减半,所以时间复杂度为 \(O(q\log P)\)
而预处理只需要预处理质数的答案,对数相加相当于完全积性函数,直接相乘。
考虑预处理 \(g^{-kB},k\in 0\sim B-1\) 需要 \(O(B)\) 的话,查询就是 \(O(\frac{\sqrt P}{\ln \sqrt{P}}\cdot \frac{P}{B})\)
平衡两者复杂度,此时实测取 \(B=\sqrt{\frac{P\sqrt{P}}{\ln P}}\) 常数更优,不过理论是取 \(B=\sqrt{\frac{P\sqrt{P}}{\ln \sqrt{P}}}\)
代码
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int p=2667433;
int mod,g,inv,m,lim,lg[p],lg_1,prime[p],Cnt,Q;
struct Linked_Hash{
struct node{int y,w,next;}E[p]; int Et,hs[p];
void Clear(){Et=0,memset(hs,-1,sizeof(hs));}
void Insert(int w,int x){E[++Et]=(node){x,w,hs[w%p]},hs[w%p]=Et;}
int locate(int W){
for (int i=hs[W%p];~i;i=E[i].next)
if (E[i].w==W) return E[i].y;
return -1;
}
}ha;
int ksm(int x,int y){
int ans=1;
for (;y;y>>=1,x=1ll*x*x%mod)
if (y&1) ans=1ll*ans*x%mod;
return ans;
}
int BSGS(int now){
for (int i=0;i<=mod/lim;++i){
int j=ha.locate(now);
if (j>=0) return i*lim+j;
now=1ll*now*inv%mod;
}
return -1;
}
int LG(int x){
if (x<=m) return lg[x];
int y=mod%x;
if (y<x-y) return (mod-1ll+lg_1+LG(y)-lg[mod/x])%(mod-1);
else return (LG(x-y)+mod-1-lg[mod/x+1])%(mod-1);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>mod>>g;
if (mod==2){
for (cin>>Q;Q;--Q) cout<<0<<'\n';
return 0;
}
m=min((int)sqrt(mod)+1,mod-1),lim=min(mod-1,(int)sqrt(1ll*mod*m/log(mod))),
ha.Clear(),inv=ksm(g,mod-1-lim),lg_1=(mod-1)>>1,lg[1]=0;
for (int i=0,now=1;i<lim;++i) ha.Insert(now,i),now=1ll*now*g%mod;
for (int i=2;i<=m;++i){
if (!lg[i]) lg[i]=BSGS(i),prime[++Cnt]=i;
for (int j=1;j<=Cnt&&prime[j]*i<=m;++j){
lg[i*prime[j]]=(lg[i]+lg[prime[j]])%(mod-1);
if (i%prime[j]==0) break;
}
}
for (cin>>Q;Q;--Q){
int x; cin>>x;
cout<<LG(x)<<'\n';
}
return 0;
}

浙公网安备 33010602011771号