【模板】基于值域预处理的快速离散对数
记使得 的最小非负 为 ,有 。
令 ,预处理 以内的 。线性筛,只需要 bsgs 求出 以内 个质数的 ,就可以 求出 以内所有 。哈希表,预处理时 次插入, 次 的查询,取 。
查询 时,如果 直接得出,否则有 ,。。又因为有 ,。 或 已知, 提前算,,选 和 较小的一边递归可以做到 查询。
复杂度
code
int mod,g,n,q;
int inc(int u,int v){((u+=v)>=mod-1)&&(u-=mod-1);return u;}
inline int ksm(int a,int b=mod-2){
int ans=1;
while(b){
if(b&1)ans=1ll*ans*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return ans;
}
int B,bas,h0;
struct hsh_table{
int head[maxn],tot;
struct nd{
int nxt;
int key;
int val;
}e[maxn];
int hsh(int u){return u%maxn;}
bool find(int key){
int u=hsh(key);
for(int i=head[u];i;i=e[i].nxt){
if(e[i].key==key)return 1;
}
return 0;
}
int &operator[](int key){
int u=hsh(key);
for(int i=head[u];i;i=e[i].nxt){
if(e[i].key==key)return e[i].val;
}
e[++tot]={head[u],key,0};head[u]=tot;
return e[tot].val;
}
void clear(){
tot=0;
for(int i=0;i<maxn;i++)head[i]=0;
}
}mp;
int bsgs(int v){
int mul=v;
for(int i=0;i<=mod/B;i++){
if(mp.find(mul))return i*B+mp[mul];
mul=1ll*mul*bas%mod;
}
}
/*
mod=ba+c
log(a)=log(-c)-log(b)=log(mod-1)+log(c)-log(b)
log(a)=log(a-c)-log(b+1)
min(c,a-c)<=a/2
*/
int h[maxn];
int sovle(int a){
int b=mod/a,c=mod%a;
if(a<=n)return h[a];
if(c<a-c)return inc(inc(h0,sovle(c)),(mod-1-h[b]));
else return inc(sovle(a-c),mod-1-h[b+1]);
}
bool vis[maxn];
int pre[maxn],cnt;
void work(){
mod=read();g=read();n=sqrt(mod)+1;B=sqrt(1ll*mod*n/log2(n));
// cout<<n<<" "<<B<<"\n";
int mul=1;for(int i=0;i<B;i++){
mp[mul]=i;
mul=1ll*mul*g%mod;
}
bas=ksm(ksm(g,B));
h0=bsgs(mod-1);
h[1]=0;
for(int i=2;i<=n;i++){
if(!vis[i]){
h[i]=bsgs(i);
pre[++cnt]=i;
}
for(int j=1;j<=cnt&&1ll*i*pre[j]<=n;j++){
vis[i*pre[j]]=0;
h[i*pre[j]]=(h[i]+h[pre[j]])%(mod-1);
if(i%pre[j]==0)break;
}
}
q=read();
while(q--){
int x=read();
printf("%lld\n",sovle(x));
}
}