1 条题解
-
0
自动搬运
来自洛谷,原作者为

离散小波变换°
有志不在年高,无志空长百岁搬运于
2025-08-24 23:04:56,当前版本为作者最后更新于2024-10-06 18:05:35,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
前段时间做区域赛题碰到几个可以用离散对数转换成卷积的题,发现需要对 求出离散对数。发现还不会这个科技,所以学习一下。
思路
记 表示 在模 意义下以 为原根的离散对数。
先解决求出 内每个数离散对数值的问题。
容易发现 。假如我们要求出 ,可以利用筛法,这样只需要求出 个素数处的离散对数值即可,其中 表示 以内素数个数。
求解一个数的离散对数,通常使用大步小步算法(BSGS)。记 ,其中 。那么有:
$$\begin{aligned} g^{Bi+j} &\equiv y &\pmod P \\ g^{j} &\equiv y\times g^{-Bi} &\pmod P \\ \end{aligned} $$所以我们枚举 ,把 插入哈希表里,接着枚举 ,查询哈希表里有没有对应的 即可。复杂度为 ,通常取 使得总复杂度为 。
然而我们这次一共要对 个数字求离散对数。事实上,我们向哈希表里插入元素的复杂度为 ,查询的复杂度为 ,其实取 才取得最优复杂度。这一步值得注意,有些复杂度错误的批量求解离散对数的做法就是块长取错了。
现在我们要多次询问 的离散对数。有一个小技巧:
先预处理出 内所有数的离散对数。对于每次询问,若 则直接回答,否则设 ,其中 且 。
根据 可知 ,从而得到:
$$\log y \equiv \log(-r)-\log v\equiv \log(P-1)+\log r-\log v $$根据 可知 ,从而得到:
其中 或是 都可直接查表获得。由于 ,所以每次迭代都能使得 的规模减半,直到 查表回答。
于是我们得到了一个 复杂度预处理, 回答单次询问的做法。
参考代码
#include<bits/stdc++.h> using namespace std; int power(int a, int b, int p){ int r = 1; while(b){ if(b & 1) r = 1ll * r * a % p; b >>= 1, a = 1ll * a * a % p; } return r; } namespace BSGS { unordered_map <int, int> M; int B, U, P, g; void init(int g, int P0, int B0){ M.clear(); B = B0; P = P0; U = power(power(g, B, P), P - 2, P); int w = 1; for(int i = 0;i < B;++ i){ M[w] = i; w = 1ll * w * g % P; } } int solve(int y){ int w = y; for(int i = 0;i <= P / B;++ i){ if(M.count(w)){ return i * B + M[w]; } w = 1ll * w * U % P; } return -1; } } const int MAXN = 1e7 + 3; int H[MAXN], P[MAXN], H0, p, h, g, mod; bool V[MAXN]; int solve(int x){ if(x <= h){ return H[x]; } int v = mod / x, r = mod % x; if(r < x - r){ return ((H0 + solve(r)) % (mod - 1) - H[v] + mod - 1) % (mod - 1); } else { return (solve(x - r) - H[v + 1] + mod - 1) % (mod - 1); } } int main(){ ios :: sync_with_stdio(false); cin.tie(nullptr); int T; cin >> mod >> g; h = sqrt(mod) + 1; BSGS :: init(g, mod, sqrt(1ll * mod * sqrt(mod) / log(mod))); H0 = BSGS :: solve(mod - 1); H[1] = 0; for(int i = 2;i <= h;++ i){ if(!V[i]){ P[++ p] = i; H[i] = BSGS :: solve(i); } for(int j = 1;j <= p && P[j] <= h / i;++ j){ int &p = P[j]; H[i * p] = (H[i] + H[p]) % (mod - 1); V[i * p] = true; if(i % p == 0) break; } } cin >> T; while(T --){ int x, tmp = 0; cin >> x; cout << solve(x) << "\n"; } return 0; }Bonus:对于 NTT 模数存在更加优秀的做法,但我太懒了还没学,欢迎大家在题解区补充。
- 1
信息
- ID
- 10864
- 时间
- 1500ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者