首先单次查询离散对数可以考虑 BSGS,将 g0,gB,g2B,g3B,…,gP/B×B 插入哈希表,然后枚举 i=0,1,2,…,B−1 在哈希表中查询 y×g−i 即可。
现在我们考虑求出 log1,log2,…,logn 怎么做。显然这个离散对数也满足 logab=loga+logb,所以我们只需要求出质数位置上的离散对数即可。
那么直接套用 BSGS 的做法,预处理时间复杂度为 O(P/B),所有查询时间复杂度为 O(lnnnB),Plnn=nB2,平衡得到 B=nPlnn。
考虑怎么预处理一些东西来快速回答询问。假设我们预处理了 P+1 以内的 log 值,现在要求 logy,那么若 y≤P+1 就直接返回,否则令 k,r 分别为 P 除以 y 的商和余数,考虑 r 和 y−r 之中一定有一个不大于 y 的一半,如果能够规约到对应的子问题上,就可以做到单次查询 O(logP)。接下来有对应的两种方式:
- 将模数 P 表示为 ky+r,变形得到 y=kP−r=k−r,那么 logy=log(−r)−logk=log(−1)+logr−logk。
- 将模数 P 表示为 (k+1)y−(y−r),变形得到 y=k+1P+y−r=k+1y−r,那么 logy=log(y−r)−log(k+1)。
通过 y>P+1 可以得出 k≤P,那么 logk,log(k+1) 可以直接查表,选择对应的子问题递归下去即可。
回到前面预处理的部分,将 n=P 代入,得到 B=P1/4ln1/2P。则预处理部分时间复杂度为 O(ln1/2PP3/4),可以通过。
代码