洛谷P11175 【模板】基于值域预处理的快速离散对数

题目链接:https://www.luogu.com.cn/problem/P11175

解题思路完全来自 oi.wiki

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;

int fadd(int x, int y, int p) { return (x + y) % p; }
int fsub(int x, int y, int p) { return (x - y + p) % p; }
int fmul(int x, int y, int p) { return 1ll * x * y % p; }
int fpow(int a, int b, int p) {
    int res = 1;
    for (int t = a % p; b; b >>= 1, t = fmul(t, t, p))
        if (b & 1)
            res = fmul(res, t, p);
    return res;
}

unordered_map<int, int> mp;
int P, g, L, B, inv, P_1;
int Lg[maxn], p[maxn];
bool vis[maxn];

int cal(int x) {
    for (int i = 0, s = x; i <= P / B; i++) {
        if (mp.count(s)) {
            return i * B + mp[s];
        }
        s = fmul(s, inv, P);
    }
    assert(1 == 0);
    return -1;
}

void init() {
    L = sqrt(P) + 1;
    B = sqrtl(1ll * P * sqrt(P) / log(P));
    inv = fpow(fpow(g, B, P), P - 2, P); // inv = 1 / (g ^ B)
    P_1 = (P - 1) / 2;  // g ^ {P_1} = P - 1 (mod P)

    for (int i = 0, s = 1; i < B; i++) {
        if (mp.count(s))
            break;
        mp[s] = i;
        s = fmul(s, g, P);
    }

    int idx = 0;
    for (int i = 2; i <= L; i++) {
        if (!vis[i]) {
            p[++idx] = i;
            Lg[i] = cal(i);
        }
        for (int j = 1; j <= idx && p[j] * i <= L; j++) {
            vis[ p[j] * i ] = true;
            Lg[ p[j] * i ] = fadd(Lg[ p[j] ], Lg[i], P - 1);
            if (i % p[j] == 0)
                break;
        }
    }
}

int solve(int y) {
    if (y <= L) return Lg[y];
    int v = P / y, r = P % y;
    if (r < y - r)
        return fadd(fsub(solve(r), Lg[v], P-1), P_1, P-1);
    else
        return fsub(solve(y-r), Lg[v+1], P-1);
}

int main() {
    scanf("%d%d", &P, &g);
    init();
    int q, y;
    scanf("%d", &q);
    while (q--) {
        scanf("%d", &y);
        printf("%d\n", solve(y));
    }
    return 0;
}
posted @ 2026-05-10 10:16  quanjun  阅读(12)  评论(0)    收藏  举报