UOJ86 mx的组合数

作者:

在

没看题解A的第一道多项式题…开心qwq

还是一遍A的

$$ \text{直接考虑卢卡斯定理。} \\ \text{首先考虑求出来模}p\text{为0的方案数。} \\ \text{设}n\text{的}p\text{进制下为}n_1n_2….n_k \\ \left( \begin{array}{c} x\\ n\\ \end{array} \right) =\left( \begin{array}{c} x_1\\ n_1\\ \end{array} \right) \left( \begin{array}{c} x_2\\ n_2\\ \end{array} \right) …\left( \begin{array}{c} x_k\\ n_k\\ \end{array} \right) \\ \text{显然如果存在任意}x_i<n_i,\text{则}\left( \begin{array}{c} x\\ n\\ \end{array} \right) =0\left( \text{mod }p \right) \\ \text{设}f\left( n,k \right) \text{表示考虑低}n\text{位,是否卡中}x\text{的上界时的结果多项式。} \\ \text{令}g\text{为模}p\text{意义下的原根,这个多项式的}i\text{次项的意义是选定的组合数在模}p\text{意义下为}g^i\text{的方案数。} \\ f\left( -\text{1,}true \right) =f\left( -\text{1,}false \right) =1 \\ \text{对于}i\ge \text{0,}f\left( i,false \right) =\left( \sum_{n_i\le k\le p-1}{x^y\left( \text{其中}g^y=\left( \begin{array}{c} k\\ n_i\\ \end{array} \right) \,\,\text{mod }p \right)} \right) f\left( i-\text{1,}false \right) \\ f\left( i,true \right) =x^y\left( \text{其中}g^y=\left( \begin{array}{c} x_i\\ n_i\\ \end{array} \right) \text{mod }p \right) f\left( i-\text{1,}true \right) +\left( \sum_{n_i\le k\le x_i-1}{x^y\left( \text{其中}g^y=\left( \begin{array}{c} k\\ n_i\\ \end{array} \right) \,\,\text{mod }p \right)} \right) f\left( i-\text{1,}false \right) \\ \text{所有卷积均为指数模}\varphi \left( p \right) \text{意义下的循环卷积。} \\ \text{最终}f\left( \lceil \log _px \rceil ,true \right) \text{即为结果多项式,}k\text{次项前的系数表示使得}\left( \begin{array}{c} x\\ n\\ \end{array} \right) =g^k\text{的方案数}\left( \text{不包括}0 \right) \text{。} \\ \text{复杂度}O\left( p\log p\log r \right) \\ \\ $$

#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <vector>
using int_t = long long int;
using lint_t = __int128;

using std::cin;
using std::cout;
using std::endl;

const int_t mod = 998244353;
const int_t g0 = 3;
const int_t LARGE = 1 << 16;
int revs[17][LARGE];
//模p意义下的某个数对应的原根的指数
int_t map[LARGE];
int_t fact[LARGE];
std::vector<int_t> ndiv;
int_t p, g, phi;
lint_t n;
int_t power(int_t base, int_t index, int_t mod = ::mod);
void transform(int_t* A, int_t len, int_t arg);
template <class T>
void read(T& x) {
    x = 0;
    auto chr = getchar();
    while (isdigit(chr) == false) {
        chr = getchar();
    }
    while (isdigit(chr)) {
        x = x * 10 + chr - '0';
        chr = getchar();
    }
}
int_t bitRev(int_t x, int_t len) {
    int_t result = 0;
    for (int i = 1; i < len; i++) {
        result |= (x & 1);
        x >>= 1;
        result <<= 1;
    }
    return result | (x & 1);
}
int_t upper2n(int_t x) {
    int_t result = 1;
    while (result < x) result *= 2;
    return result;
}
void poly_mul(const int_t* A, const int_t* B, int_t* C) {
    static int_t Ax[LARGE], Bx[LARGE];
    std::copy(A, A + p, Ax);
    std::copy(B, B + p, Bx);
#ifdef DEBUG
    cout << "mul " << endl;
    for (int i = 0; i < p; i++) cout << A[i] << " ";
    cout << endl;
    for (int i = 0; i < p; i++) cout << B[i] << " ";
    cout << endl;

#endif
    int_t size2 = upper2n(2 * p);
    for (int_t i = p; i < size2; i++) Ax[i] = Bx[i] = 0;

    transform(Ax, size2, 1);
    transform(Bx, size2, 1);
    for (int_t i = 0; i < size2; i++) Ax[i] = Ax[i] * Bx[i] % mod;
    transform(Ax, size2, -1);
#ifdef DEBUG
    cout << "mul got ";
    for (int i = 0; i < size2; i++)
        cout << Ax[i] * power(size2, -1) % mod << " ";
    cout << endl;
#endif
    for (int_t i = phi; i < size2; i++) {
        Ax[i % phi] = (Ax[i % phi] + Ax[i]) % mod;
    }
    const auto inv = power(size2, -1, mod);
#ifdef DEBUG
    cout << "C = ";
#endif
    for (int_t i = 0; i < phi; i++) {
        C[i] = Ax[i] * inv % mod;
#ifdef DEBUG
        cout << C[i] << " ";
#endif
    }
}
const int_t OFFSET = 1;
std::vector<int_t> div(lint_t x) {
    std::vector<int_t> result;
    while (x) {
        result.push_back(x % p);
        x /= p;
    }
    return result;
}
int_t C(int_t n, int_t m) {
    return ((fact[n] - fact[m] - fact[n - m]) % phi + phi) % phi;
}
void process(lint_t x, int_t* output) {
    static int_t dp[103][2][LARGE];
    memset(dp, 0, sizeof(dp));
    dp[-1 + OFFSET][0][0] = dp[-1 + OFFSET][1][0] = 1;
    auto xdiv = div(x);
    int_t size = std::max(xdiv.size(), ndiv.size()) + 1;
    xdiv.resize(size);
    ndiv.resize(size);
    for (int_t i = 0; i < size; i++) {
        // false
        {
            static int_t poly[LARGE];
            std::fill(poly, poly + p, 0);
            for (int_t j = ndiv[i]; j <= p - 1; j++) {
                poly[C(j, ndiv[i])] += 1;
            }
            poly_mul(poly, dp[i - 1 + OFFSET][0], dp[i + OFFSET][0]);
        }
        // true
        {
            auto& curr = dp[i + OFFSET][1];
            static int_t poly[LARGE], output[LARGE];
            std::fill(poly, poly + p, 0);
            for (int_t j = ndiv[i]; j <= xdiv[i] - 1; j++)
                poly[C(j, ndiv[i])] += 1;
            poly_mul(poly, dp[i - 1 + OFFSET][0], output);
            std::fill(poly, poly + p, 0);
            for (int j = 0; j < phi; j++) curr[j] = (curr[j] + output[j]) % mod;
            if (xdiv[i] >= ndiv[i]) poly[C(xdiv[i], ndiv[i])] += 1;
            poly_mul(poly, dp[i - 1 + OFFSET][1], output);
            for (int j = 0; j < phi; j++) curr[j] = (curr[j] + output[j]) % mod;
        }
    }
    int_t zero = x % mod;
    for (int i = 0; i < phi; i++) {
        int_t x = power(g, i, p);
        output[x] = (output[x] + dp[size - 1 + OFFSET][1][i]) % mod;
        zero = (zero - dp[size - 1 + OFFSET][1][i] + mod) % mod;
    }
    output[0] = zero;
}
int main() {
    for (int i = 1; i <= 16; i++) {
        for (int j = 0; j < (1 << i); j++) {
            revs[i][j] = bitRev(j, i);
        }
    }
    lint_t left, right;
    read(p);
    read(n);
    read(left);
    read(right);
    phi = p - 1;
    ndiv = div(n);
    std::vector<int_t> divisors;
    for (int_t i = 2; i * i <= phi; i++) {
        if (phi % i == 0) {
            divisors.push_back(i);
            if (i * i != phi) divisors.push_back(phi / i);
        }
    }
    for (g = 2; g <= p - 1; g++) {
        bool ok = true;
        for (int_t x : divisors) {
            if (power(g, x, p) == 1 && x != phi) {
                ok = false;
                break;
            }
        }
        if (ok) {
            break;
        }
    }
    for (int i = 0; i < phi; i++) map[power(g, i, p)] = i;
    for (int_t i = 2; i < p; i++) fact[i] = (fact[i - 1] + map[i]) % phi;
    static int_t rights[LARGE], lefts[LARGE];
    process(right, rights);
    process(left - 1, lefts);
    for (int i = 0; i < p; i++) {
        printf("%lld\n", (rights[i] - lefts[i] + mod) % mod);
    }
    return 0;
}

void transform(int_t* A, int_t len, int_t arg) {
    int_t size2 = log2(len);
    for (int i = 0; i < len; i++) {
        int x = revs[size2][i];
        if (x > i) std::swap(A[i], A[x]);
    }
    for (int i = 2; i <= len; i *= 2) {
        int_t mr = power(g0, arg * (mod - 1) / i, mod);
        for (int_t j = 0; j < len; j += i) {
            int_t curr = 1;
            for (int_t k = 0; k < i / 2; k++) {
                int_t u = A[j + k], t = A[j + k + i / 2] * curr % mod;
                A[j + k] = (u + t) % mod;
                A[j + k + i / 2] = (u - t + mod) % mod;
                curr = curr * mr % mod;
            }
        }
    }
}

int_t power(int_t base, int_t index, int_t mod) {
    int_t result = 1;
    if (index < 0) index = (index % (mod - 1) + mod - 1) % (mod - 1);
    while (index) {
        if (index & 1) result = result * base % mod;
        base = base * base % mod;
        index >>= 1;
    }
    return result;
}

 

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理。