#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;
}
posted @ 2026-09-17 01:48  lemondinosaur  阅读(4)  评论(0)    收藏  举报