I Infinity

题意

是所有 阶置换构成的集合. 对于 ,设 是集合 中的元素数量.

固定 ,多组询问 ,请计算

答案对 取模.

前置定义

这部分内容主要是为了防止符号上的歧义. 如果您已了解这些概念,可以跳过这部分内容.

置换

阶置换指的是从 的双射.

例如, 是一个 4 阶置换.

其可以记作

当然,其第一行不一定必须是 的顺序,只要第一行的值被映射到第二行对应的值即可. 但习惯上,我们固定第一行的顺序.

为了方便表示,我们采用下面的列表形式来简写

置换的复合

置换之间可以进行复合,定义为

例如若 ,则

可以看到 还是一个 4 阶置换.

原因是, 2 个从 到自身的双射的复合,仍然是从 到自身的双射,即 阶置换.

因此

置换的循环分解

对于置换 ,我们可以建立一个有向图 ,其中 .

例如下图是 的有向图:

alt

可以看到,这个图由若干个简单有向环组成.

每个 阶置换都对应一个点集是 ,由若干个简单有向环组成的有向图.

原因是,由于这是一个从 到自身的双射,所以每个点的出度和入度都为 1,因此每个点恰好属于一个简单有向环,且没有多余的入边和出边.

反过来,每个点集是 ,由若干个简单有向环组成的有向图,都对应一个 阶置换.

原因是,由于每个点恰好属于一个简单有向环,所以每个点的出度和入度都为 1,且没有多余的入边和出边,所以这是一个从 到自身的双射,即 阶置换.

我们可以把每个排列的有向图分解为多个有向图:对于每个简单有向环,我们保持这个该环上的结点的边不变,而不在该环上的结点的边全部删去,并添加不在该环上的结点到自身的自环.

这样我们就得到了多个有向图,每个有向图对应一个置换,称为循环.

我们这样定义循环的符号表示: 是一个 阶置换,满足

例如 作为一个 阶置换时,即为 .

由于这些环之间是互不干扰的,因此这些有向图对应的置换,以任意顺序复合,得到的都是原置换.

这样,我们就把原置换分解为了若干个置换的复合,这些置换都是循环,且互不相交.

这种分解方式称为置换的循环分解.

如果我们不考虑这些置换的顺序,那么就可以说,一个置换的循环分解的结果是唯一的.

的循环分解可写为

可以看到,这里的每个循环恰好对应有向图上的一个简单有向环.

恒等置换

阶恒等置换指的是 到自身的恒等映射,即:

如果 阶置换 满足对任意 ,都有 ,则称 阶恒等置换.

例如 是 3 阶恒等置换.

逆置换

阶置换 的逆置换 指的是 的逆映射,即:

对于 阶置换 ,如果映射 满足对任意 ,都有

则称 的逆置换,记作 .

由于双射的逆映射还是双射,因此,置换的逆置换 是置换.

由于双射的逆映射存在且唯一,因此任意一个置换的逆置换,都存在且唯一.

求解逆置换是简单的. 下面举例说明:如果 ,则

因此

由定义不难得到:

  1. 都是恒等置换

题解

首先应该明确,对于指定的 ,集合 里面的元素到底是什么.

先从简单的例子开始考虑,例如 ,我们可以遍历所有的 4 阶置换 ,列表如下:

的循环分解
[1, 2, 3, 4] [1, 2, 3, 4] [1, 3, 4, 2] (1)(2 3 4)
[1, 2, 4, 3] [1, 2, 4, 3] [1, 4, 2, 3] (1)(2 4 3)
[1, 3, 2, 4] [1, 3, 2, 4] [1, 4, 2, 3] (1)(2 4 3)
[1, 3, 4, 2] [1, 4, 2, 3] [1, 3, 4, 2] (1)(2 3 4)
[1, 4, 2, 3] [1, 3, 4, 2] [1, 3, 4, 2] (1)(2 3 4)
[1, 4, 3, 2] [1, 4, 3, 2] [1, 4, 2, 3] (1)(2 4 3)
[2, 1, 3, 4] [2, 1, 3, 4] [3, 2, 4, 1] (1 3 4)(2)
[2, 1, 4, 3] [2, 1, 4, 3] [4, 2, 1, 3] (1 4 3)(2)
[2, 3, 1, 4] [3, 1, 2, 4] [2, 4, 3, 1] (1 2 4)(3)
[2, 3, 4, 1] [4, 1, 2, 3] [2, 3, 1, 4] (1 2 3)(4)
[2, 4, 1, 3] [3, 1, 4, 2] [4, 1, 3, 2] (1 4 2)(3)
[2, 4, 3, 1] [4, 1, 3, 2] [3, 1, 2, 4] (1 3 2)(4)
[3, 1, 2, 4] [2, 3, 1, 4] [4, 2, 1, 3] (1 4 3)(2)
[3, 1, 4, 2] [2, 4, 1, 3] [3, 2, 4, 1] (1 3 4)(2)
[3, 2, 1, 4] [3, 2, 1, 4] [4, 1, 3, 2] (1 4 2)(3)
[3, 2, 4, 1] [4, 2, 1, 3] [3, 1, 2, 4] (1 3 2)(4)
[3, 4, 1, 2] [3, 4, 1, 2] [2, 4, 3, 1] (1 2 4)(3)
[3, 4, 2, 1] [4, 3, 1, 2] [2, 3, 1, 4] (1 2 3)(4)
[4, 1, 2, 3] [2, 3, 4, 1] [3, 2, 4, 1] (1 3 4)(2)
[4, 1, 3, 2] [2, 4, 3, 1] [4, 2, 1, 3] (1 4 3)(2)
[4, 2, 1, 3] [3, 2, 4, 1] [2, 4, 3, 1] (1 2 4)(3)
[4, 2, 3, 1] [4, 2, 3, 1] [2, 3, 1, 4] (1 2 3)(4)
[4, 3, 1, 2] [3, 4, 2, 1] [4, 1, 3, 2] (1 4 2)(3)
[4, 3, 2, 1] [4, 3, 2, 1] [3, 1, 2, 4] (1 3 2)(4)

我们发现一个事实,就是这个集合里的所有排列,他的有向图(可从循环分解看出)都是 1 个一元环 + 1 个三元环.

换句话说,这些图都是同构的.

如果你不知道同构的概念,可以理解为,这些图的区别只是把结点重新编号了一下. 如果去除结点编号,则这些图是无法区分的.

这里,给出图的同构的形式化定义:

是 2 个图,如果存在一个双射 ,使得对任意 ,都有

则称 同构.

由于我们研究的是 2 个 阶置换对应的有向图的同构关系,因此 ,则这里的双射 .

我们猜测: 置换 的充分必要条件是, 的有向图与 的有向图同构.

要证明这个结论,只需要从图同构的定义出发.

  1. 必要性:若 ,则 的有向图与 的有向图同构.

    ,记 的有向图边集是 的有向图边集是 ,则

    因此 的有向图与 的有向图同构.

    这就证明了必要性.

  2. 充分性:若 的有向图与 的有向图同构,则 .

    的有向图边集是 的有向图边集是 ,则存在 ,使得

    因此 .

    这就证明了充分性.

因此, 就是有向图与 的有向图同构的置换的集合, 就是有向图与 的有向图同构的置换的个数.

为了加强对这个概念的理解,这里以 举例说明:

有向图 置换
4 个一元环 1
2 个一元环,1 个二元环 , , , , , 6
1 个一元环,1 个三元环 , , , , , , , 8
2 个二元环 , , 3
1 个四元环 , , , , , 6

因此,此时的答案为

例如 时有

这就是样例 1 的第 4 个数据的答案.

从这个例子,我们知道,我们可以按有向图里的简单有向环的情况来把 划分成若干个等价类,每个等价类的大小如果是 ,则对答案会产生 的贡献.

接下去,本题就变成纯组合数学问题了.

首先, 个点,能分成哪些环?

我们可以设 元环有 个,则有约束

均为非负整数.

我们需要看的就是满足上述约束的所有情况.

对于某一组 的情况,我们要知道,有向图是这种情况的置换,有多少个. 实际上我们就是要求这样的有向图有多少个.

如果同大小的环也视为地位不等的,那么,我们把 个数字分给这些环,利用组合数相乘等方法可得有

种分法. 但是,事实上同大小的环是地位相等的,因此实际上是

种分法. 然后,每个大小为 的环有 种情况(固定一个起点的值,剩下部分全排列),因此,这样的有向图有

因此,我们的答案是

这里最难处理的就是,我们到底怎样处理这个 的约束条件.

我们知道

是把 定为 后产生的乘积项.

如果我们忽略 的约束条件,只要求 是非负整数,我们就有

这是因为,这 个和式乘起来展开后的每一项,都是这 个和式中,每个选 1 项,乘起来得到的,相当于对 的一种取值,产生的项. 那我们把这些全部加起来,就得到了总的和.

现在,我们要添加 这个条件. 这个是不难处理的,我们可以给每个项加个指示部分 ,这样 个和式乘起来展开后的每一项, 的指数就指示了该项对应的 的值,我们只需要知道 项的系数是多少就可以了. 也就是说,

这里 这个记号的含义是 项的系数.

由于题目要对多个 进行询问,我们把外层乘积的上限改为 ,显然大于 次的 产生的项不会影响 的系数.

这样我们右边这个多项式部分就与 无关了.

那么,我们的答案是

的查询上限是 ,我们的难点主要是,要求出

这个多项式的不超过 次的项的系数.

以下全程的计算都只需要 次项的系数,可以认为是在 下进行的运算.

如果直接使用 NTT 多项式乘法,由于内层求和需要保留到 项,外层乘积需要保留到 ,而 2 个 次多项式乘法的复杂度是 ,则总复杂度是 ,完全无法接受.

我们考虑引入自然对数运算 ,这样,就变为了

但是,计算一个 次多项式的自然对数,是 的,如果对 个多项式都算一遍自然对数,这仍然是 ,无法接受.

因此,我们考虑能不能直接算出 的系数.

我们发现

因此我们可以设

该多项式不超过 次的项的系数可以以 的时间复杂度计算.

那么我们可以以 的时间复杂度计算出 的所有 .

那么

我们只需要遍历 的部分即可,而这个的时间复杂度是

然后,我们就能以 的时间复杂度计算出

然后就能 查询答案

时间复杂度: 预处理, 每次查询.

空间复杂度:

代码

#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned;

using u128 = unsigned __int128;
using i128 = __int128;

template<class T>
constexpr T power(T a, i64 b) {
    T res = 1;
    for (; b; b /= 2, a *= a) {
        if (b % 2) {
            res *= a;
        }
    }
    return res;
}

template<int P>
struct MInt {
    int x;
    constexpr MInt() : x{} {}
    constexpr MInt(i64 x) : x{norm(x % getMod())} {}
    
    static int Mod;
    constexpr static int getMod() {
        if (P > 0) {
            return P;
        } else {
            return Mod;
        }
    }
    constexpr static void setMod(int Mod_) {
        Mod = Mod_;
    }
    constexpr int norm(int x) const {
        if (x < 0) {
            x += getMod();
        }
        if (x >= getMod()) {
            x -= getMod();
        }
        return x;
    }
    constexpr int val() const {
        return x;
    }
    explicit constexpr operator int() const {
        return x;
    }
    constexpr MInt operator-() const {
        MInt res;
        res.x = norm(getMod() - x);
        return res;
    }
    constexpr MInt inv() const {
        assert(x != 0);
        return power(*this, getMod() - 2);
    }
    constexpr MInt &operator*=(MInt rhs) & {
        x = 1LL * x * rhs.x % getMod();
        return *this;
    }
    constexpr MInt &operator+=(MInt rhs) & {
        x = norm(x + rhs.x);
        return *this;
    }
    constexpr MInt &operator-=(MInt rhs) & {
        x = norm(x - rhs.x);
        return *this;
    }
    constexpr MInt &operator/=(MInt rhs) & {
        return *this *= rhs.inv();
    }
    friend constexpr MInt operator*(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res *= rhs;
        return res;
    }
    friend constexpr MInt operator+(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res += rhs;
        return res;
    }
    friend constexpr MInt operator-(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res -= rhs;
        return res;
    }
    friend constexpr MInt operator/(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res /= rhs;
        return res;
    }
    friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {
        i64 v;
        is >> v;
        a = MInt(v);
        return is;
    }
    friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {
        return os << a.val();
    }
    friend constexpr bool operator==(MInt lhs, MInt rhs) {
        return lhs.val() == rhs.val();
    }
    friend constexpr bool operator!=(MInt lhs, MInt rhs) {
        return lhs.val() != rhs.val();
    }
};

template<>
int MInt<0>::Mod = 1;

template<int V, int P>
constexpr MInt<P> CInv = MInt<P>(V).inv();

constexpr int P = 998244353;
using Z = MInt<P>;

std::vector<int> rev;
template<int P>
std::vector<MInt<P>> roots{0, 1};

template<int P>
constexpr MInt<P> findPrimitiveRoot() {
    MInt<P> i = 2;
    int k = __builtin_ctz(P - 1);
    while (true) {
        if (power(i, (P - 1) / 2) != 1) {
            break;
        }
        i += 1;
    }
    return power(i, (P - 1) >> k);
}

template<int P>
constexpr MInt<P> primitiveRoot = findPrimitiveRoot<P>();

template<>
constexpr MInt<998244353> primitiveRoot<998244353> {31};

template<int P>
constexpr void dft(std::vector<MInt<P>> &a) {
    int n = a.size();
    
    if (int(rev.size()) != n) {
        int k = __builtin_ctz(n) - 1;
        rev.resize(n);
        for (int i = 0; i < n; i++) {
            rev[i] = rev[i >> 1] >> 1 | (i & 1) << k;
        }
    }
    
    for (int i = 0; i < n; i++) {
        if (rev[i] < i) {
            std::swap(a[i], a[rev[i]]);
        }
    }
    if (roots<P>.size() < n) {
        int k = __builtin_ctz(roots<P>.size());
        roots<P>.resize(n);
        while ((1 << k) < n) {
            auto e = power(primitiveRoot<P>, 1 << (__builtin_ctz(P - 1) - k - 1));
            for (int i = 1 << (k - 1); i < (1 << k); i++) {
                roots<P>[2 * i] = roots<P>[i];
                roots<P>[2 * i + 1] = roots<P>[i] * e;
            }
            k++;
        }
    }
    for (int k = 1; k < n; k *= 2) {
        for (int i = 0; i < n; i += 2 * k) {
            for (int j = 0; j < k; j++) {
                MInt<P> u = a[i + j];
                MInt<P> v = a[i + j + k] * roots<P>[k + j];
                a[i + j] = u + v;
                a[i + j + k] = u - v;
            }
        }
    }
}

template<int P>
constexpr void idft(std::vector<MInt<P>> &a) {
    int n = a.size();
    std::reverse(a.begin() + 1, a.end());
    dft(a);
    MInt<P> inv = (1 - P) / n;
    for (int i = 0; i < n; i++) {
        a[i] *= inv;
    }
}

template<int P = 998244353>
struct Poly : public std::vector<MInt<P>> {
    using Value = MInt<P>;
    
    Poly() : std::vector<Value>() {}
    explicit constexpr Poly(int n) : std::vector<Value>(n) {}
    
    explicit constexpr Poly(const std::vector<Value> &a) : std::vector<Value>(a) {}
    constexpr Poly(const std::initializer_list<Value> &a) : std::vector<Value>(a) {}
    
    template<class InputIt, class = std::_RequireInputIter<InputIt>>
    explicit constexpr Poly(InputIt first, InputIt last) : std::vector<Value>(first, last) {}
    
    template<class F>
    explicit constexpr Poly(int n, F f) : std::vector<Value>(n) {
        for (int i = 0; i < n; i++) {
            (*this)[i] = f(i);
        }
    }
    
    constexpr Poly shift(int k) const {
        if (k >= 0) {
            auto b = *this;
            b.insert(b.begin(), k, 0);
            return b;
        } else if (this->size() <= -k) {
            return Poly();
        } else {
            return Poly(this->begin() + (-k), this->end());
        }
    }
    constexpr Poly trunc(int k) const {
        Poly f = *this;
        f.resize(k);
        return f;
    }
    constexpr friend Poly operator+(const Poly &a, const Poly &b) {
        Poly res(std::max(a.size(), b.size()));
        for (int i = 0; i < a.size(); i++) {
            res[i] += a[i];
        }
        for (int i = 0; i < b.size(); i++) {
            res[i] += b[i];
        }
        return res;
    }
    constexpr friend Poly operator-(const Poly &a, const Poly &b) {
        Poly res(std::max(a.size(), b.size()));
        for (int i = 0; i < a.size(); i++) {
            res[i] += a[i];
        }
        for (int i = 0; i < b.size(); i++) {
            res[i] -= b[i];
        }
        return res;
    }
    constexpr friend Poly operator-(const Poly &a) {
        std::vector<Value> res(a.size());
        for (int i = 0; i < int(res.size()); i++) {
            res[i] = -a[i];
        }
        return Poly(res);
    }
    constexpr friend Poly operator*(Poly a, Poly b) {
        if (a.size() == 0 || b.size() == 0) {
            return Poly();
        }
        if (a.size() < b.size()) {
            std::swap(a, b);
        }
        int n = 1, tot = a.size() + b.size() - 1;
        while (n < tot) {
            n *= 2;
        }
        if (((P - 1) & (n - 1)) != 0 || b.size() < 128) {
            Poly c(a.size() + b.size() - 1);
            for (int i = 0; i < a.size(); i++) {
                for (int j = 0; j < b.size(); j++) {
                    c[i + j] += a[i] * b[j];
                }
            }
            return c;
        }
        a.resize(n);
        b.resize(n);
        dft(a);
        dft(b);
        for (int i = 0; i < n; ++i) {
            a[i] *= b[i];
        }
        idft(a);
        a.resize(tot);
        return a;
    }
    constexpr friend Poly operator*(Value a, Poly b) {
        for (int i = 0; i < int(b.size()); i++) {
            b[i] *= a;
        }
        return b;
    }
    constexpr friend Poly operator*(Poly a, Value b) {
        for (int i = 0; i < int(a.size()); i++) {
            a[i] *= b;
        }
        return a;
    }
    constexpr friend Poly operator/(Poly a, Value b) {
        for (int i = 0; i < int(a.size()); i++) {
            a[i] /= b;
        }
        return a;
    }
    constexpr Poly &operator+=(Poly b) {
        return (*this) = (*this) + b;
    }
    constexpr Poly &operator-=(Poly b) {
        return (*this) = (*this) - b;
    }
    constexpr Poly &operator*=(Poly b) {
        return (*this) = (*this) * b;
    }
    constexpr Poly &operator*=(Value b) {
        return (*this) = (*this) * b;
    }
    constexpr Poly &operator/=(Value b) {
        return (*this) = (*this) / b;
    }
    constexpr Poly deriv() const {
        if (this->empty()) {
            return Poly();
        }
        Poly res(this->size() - 1);
        for (int i = 0; i < this->size() - 1; ++i) {
            res[i] = (i + 1) * (*this)[i + 1];
        }
        return res;
    }
    constexpr Poly integr() const {
        Poly res(this->size() + 1);
        for (int i = 0; i < this->size(); ++i) {
            res[i + 1] = (*this)[i] / (i + 1);
        }
        return res;
    }
    constexpr Poly inv(int m) const {
        Poly x{(*this)[0].inv()};
        int k = 1;
        while (k < m) {
            k *= 2;
            x = (x * (Poly{2} - trunc(k) * x)).trunc(k);
        }
        return x.trunc(m);
    }
    constexpr Poly log(int m) const {
        return (deriv() * inv(m)).integr().trunc(m);
    }
    constexpr Poly exp(int m) const {
        Poly x{1};
        int k = 1;
        while (k < m) {
            k *= 2;
            x = (x * (Poly{1} - x.log(k) + trunc(k))).trunc(k);
        }
        return x.trunc(m);
    }
    constexpr Poly pow(int k, int m) const {
        int i = 0;
        while (i < this->size() && (*this)[i] == 0) {
            i++;
        }
        if (i == this->size() || 1LL * i * k >= m) {
            return Poly(m);
        }
        Value v = (*this)[i];
        auto f = shift(-i) * v.inv();
        return (f.log(m - i * k) * k).exp(m - i * k).shift(i * k) * power(v, k);
    }
    constexpr Poly sqrt(int m) const {
        Poly x{1};
        int k = 1;
        while (k < m) {
            k *= 2;
            x = (x + (trunc(k) * x.inv(k)).trunc(k)) * CInv<2, P>;
        }
        return x.trunc(m);
    }
    constexpr Poly mulT(Poly b) const {
        if (b.size() == 0) {
            return Poly();
        }
        int n = b.size();
        std::reverse(b.begin(), b.end());
        return ((*this) * b).shift(-(n - 1));
    }
    constexpr std::vector<Value> eval(std::vector<Value> x) const {
        if (this->size() == 0) {
            return std::vector<Value>(x.size(), 0);
        }
        const int n = std::max(x.size(), this->size());
        std::vector<Poly> q(4 * n);
        std::vector<Value> ans(x.size());
        x.resize(n);
        std::function<void(int, int, int)> build = [&](int p, int l, int r) {
            if (r - l == 1) {
                q[p] = Poly{1, -x[l]};
            } else {
                int m = (l + r) / 2;
                build(2 * p, l, m);
                build(2 * p + 1, m, r);
                q[p] = q[2 * p] * q[2 * p + 1];
            }
        };
        build(1, 0, n);
        std::function<void(int, int, int, const Poly &)> work = [&](int p, int l, int r, const Poly &num) {
            if (r - l == 1) {
                if (l < int(ans.size())) {
                    ans[l] = num[0];
                }
            } else {
                int m = (l + r) / 2;
                work(2 * p, l, m, num.mulT(q[2 * p + 1]).trunc(m - l));
                work(2 * p + 1, m, r, num.mulT(q[2 * p]).trunc(r - m));
            }
        };
        work(1, 0, n, mulT(q[1].inv(n)));
        return ans;
    }
};

template<int P = 998244353>
Poly<P> berlekampMassey(const Poly<P> &s) {
    Poly<P> c;
    Poly<P> oldC;
    int f = -1;
    for (int i = 0; i < s.size(); i++) {
        auto delta = s[i];
        for (int j = 1; j <= c.size(); j++) {
            delta -= c[j - 1] * s[i - j];
        }
        if (delta == 0) {
            continue;
        }
        if (f == -1) {
            c.resize(i + 1);
            f = i;
        } else {
            auto d = oldC;
            d *= -1;
            d.insert(d.begin(), 1);
            MInt<P> df1 = 0;
            for (int j = 1; j <= d.size(); j++) {
                df1 += d[j - 1] * s[f + 1 - j];
            }
            assert(df1 != 0);
            auto coef = delta / df1;
            d *= coef;
            Poly<P> zeros(i - f - 1);
            zeros.insert(zeros.end(), d.begin(), d.end());
            d = zeros;
            auto temp = c;
            c += d;
            if (i - temp.size() > f - oldC.size()) {
                oldC = temp;
                f = i;
            }
        }
    }
    c *= -1;
    c.insert(c.begin(), 1);
    return c;
}


template<int P = 998244353>
MInt<P> linearRecurrence(Poly<P> p, Poly<P> q, i64 n) {
    int m = q.size() - 1;
    while (n > 0) {
        auto newq = q;
        for (int i = 1; i <= m; i += 2) {
            newq[i] *= -1;
        }
        auto newp = p * newq;
        newq = q * newq;
        for (int i = 0; i < m; i++) {
            p[i] = newp[i * 2 + n % 2];
        }
        for (int i = 0; i <= m; i++) {
            q[i] = newq[i * 2];
        }
        n /= 2;
    }
    return p[0] / q[0];
}

struct Comb {
    int n;
    std::vector<Z> _fac;
    std::vector<Z> _invfac;
    std::vector<Z> _inv;
    
    Comb() : n{0}, _fac{1}, _invfac{1}, _inv{0} {}
    Comb(int n) : Comb() {
        init(n);
    }
    
    void init(int m) {
        if (m <= n) return;
        _fac.resize(m + 1);
        _invfac.resize(m + 1);
        _inv.resize(m + 1);
        
        for (int i = n + 1; i <= m; i++) {
            _fac[i] = _fac[i - 1] * i;
        }
        _invfac[m] = _fac[m].inv();
        for (int i = m; i > n; i--) {
            _invfac[i - 1] = _invfac[i] * i;
            _inv[i] = _invfac[i] * _fac[i - 1];
        }
        n = m;
    }
    
    Z fac(int m) {
        if (m > n) init(2 * m);
        return _fac[m];
    }
    Z invfac(int m) {
        if (m > n) init(2 * m);
        return _invfac[m];
    }
    Z inv(int m) {
        if (m > n) init(2 * m);
        return _inv[m];
    }
    Z binom(int n, int m) {
        if (n < m || m < 0) return 0;
        return fac(n) * invfac(m) * invfac(n - m);
    }
} comb;

constexpr int N = 200000;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int t, k;
    std::cin >> t >> k;
    
    Poly g(N + 1);
    for (int j = 0; j <= N; j++) {
        g[j] = power(comb.invfac(j), k + 1);
    }
    g = g.log(N + 1);

    Poly f(N + 1);
    for (int i = 1; i <= N; i++) {
        Z val = power(comb.inv(i), k + 1);
        Z x = 1;
        for (int j = 0; j <= N / i; j++) {
            f[i * j] += g[j] * x;
            x *= val;
        }
    }
    f = f.exp(N + 1);

    std::vector<Z> ans(N + 1, 0);
    for (int n = 0; n <= N; ++n) {
        ans[n] = power(comb.fac(n), k + 1) * f[n];
    }

    while (t--) {
        int n;
        std::cin >> n;
        
        std::cout << ans[n] << "\n";
    }
    
    return 0;
}