Solution Set【2024.1.17】

[ABC298Ex] Sum of Min of Length

在下文的推导中假设 depthLdepthR,若不符合则交换 LR

首先我们可以发现,我们可以找到 Rdist(L,R)2 级祖先 M。可以发现,在 M 子树内的节点距离 R 更近,而在 M 子树外的节点距离 L 更近。因此我们可以将问题转化为求 M 子树内的节点到 R 的距离和 M 子树外的节点到 L 的距离和。若 MLR 的最近公共祖先那么特殊考虑即可。

考虑如何计算答案,首先以求 M 子树内的节点到 R 的距离和为例,我们不妨求出所有节点到 R 的距离和后排除非 M 子树节点的贡献。具体的,通过换根 DP 我们可以对于每个节点 u 求出 fu 代表所有节点到 u 的距离和,gu 代表其子树内的全部节点到 u 的距离和。那么 M 子树内的节点到 R 的距离和即为:

fR(fMgM)(NsizeM)×dist(M,R)

其中 N 为节点总数,sizeMM 子树的节点数。同理,M 子树外的节点到 L 的距离和为:

fLgMsizeM×dist(M,L)

直接计算即可。

Code
#include <bits/stdc++.h>

typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;

valueType N, Q;
ValueMatrix G;
ValueVector InTree, OutTree, size, depth, father, son, top;
ValueVector leftBound, rightBound, node;

void dfs(valueType x, valueType from) {
    father[x] = from;
    size[x] = 1;
    depth[x] = depth[from] + 1;
    InTree[x] = 0;

    for (auto const &to : G[x]) {
        if (to == from)
            continue;

        dfs(to, x);

        size[x] += size[to];
        InTree[x] += InTree[to] + size[to];

        if (son[x] == 0 || size[to] > size[son[x]])
            son[x] = to;
    }
}

void build(valueType x, valueType from, valueType _top) {
    static valueType dfsCount = 0;

    top[x] = _top;

    if (from != 0)
        OutTree[x] = OutTree[from] - (InTree[x] + size[x]) + (N - size[x]) + InTree[x];

    leftBound[x] = ++dfsCount;
    node[dfsCount] = x;

    if (son[x] != 0)
        build(son[x], x, _top);

    for (auto const &to : G[x]) {
        if (to == from || to == son[x])
            continue;

        build(to, x, to);
    }

    rightBound[x] = dfsCount;
}

valueType GetLCA(valueType u, valueType v) {
    while (top[u] != top[v]) {
        if (depth[top[u]] < depth[top[v]])
            std::swap(u, v);

        u = father[top[u]];
    }

    return depth[u] < depth[v] ? u : v;
}

valueType GetDist(valueType u, valueType v) {
    if (u < 1 || u > N || v < 1 || v > N)
        return 0;根据二项式反演,我们有:

因此我们可以在
的时间内求出 ,完成第二部分的计数。
valueType Jump(valueType u, valueType k) {
    while (u != 0) {
        if (depth[u] - depth[top[u]] + 1 <= k) {
            k -= depth[u] - depth[top[u]] + 1;

            u = father[top[u]];
        } else {
            return node[leftBound[u] - k];
        }
    }

    return 0;
}

valueType solve(valueType u, valueType v) {
    if (u == v)
        return OutTree[u];

    if (depth[u] > depth[v])
        std::swap(u, v);

    valueType lca = GetLCA(u, v);

    valueType const dist = GetDist(u, v);

    valueType const mid = Jump(v, dist / 2);

    valueType ans = 0;

    ans += OutTree[v];

    ans -= (OutTree[mid] - InTree[mid]) + (N - size[mid]) * GetDist(mid, v);

    ans += OutTree[u];

    ans -= InTree[mid] + size[mid] * GetDist(mid, u);

    return ans;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    std::cin >> N;

    G.resize(N + 1);
    InTree.resize(N + 1);
    OutTree.resize(N + 1);
    size.resize(N + 1);
    depth.resize(N + 1);
    father.resize(N + 1);
    son.resize(N + 1);
    top.resize(N + 1);
    leftBound.resize(N + 1);
    rightBound.resize(N + 1);
    node.resize(N + 1);

    for (valueType i = 1; i < N; ++i) {
        valueType u, v;
        std::cin >> u >> v;

        G[u].push_back(v);
        G[v].push_back(u);
    }

    dfs(1, 0);

    OutTree[1] = InTree[1];

    build(1, 0, 1);

    std::cin >> Q;

    for (valueType q = 0; q < Q; ++q) {
        valueType u, v;

        std::cin >> u >> v;

        std::cout << solve(u, v) << '\n';
    }

    std::cout << std::flush;

    return 0;
}

信封问题

定义在排列 p 中满足 pi=ii 为确定点,设 f(n,k) 表示在长度为 n 的排列中钦定有 k 个确定点的方案数,g(n,k) 表示在长度为 n 的排列中恰好有 k 个确定点的方案数。那么我们有:

f(n,k)=i=kn(ik)g(n,i)

根据二项式反演,我们有:

g(n,k)=i=kn(ik)(1)ikf(n,i)

考虑如何求 f(n,k),发现我们确定了 k 个点后,剩下的 nk 个点可以任意排列,因此我们有:

f(n,k)=(nk)(nk)!=n!k!

而我们要求的是错排数,即 g(n,0),那么我们有:

g(n,0)=i=0n(i0)(1)if(n,i)=i=0n(1)in!i!

直接计算即可,复杂度 O(n)

Code
#include <bits/stdc++.h>

typedef long long valueType;
typedef std::vector<valueType> ValueVector;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    valueType N;

    std::cin >> N;

    ValueVector Fact(N + 1, 1);

    for (valueType i = 1; i <= N; ++i)
        Fact[i] = Fact[i - 1] * i;

    valueType ans = 0;

    for (valueType i = 0; i <= N; ++i) {
        if (i & 1)
            ans -= Fact[N] / Fact[i];
        else
            ans += Fact[N] / Fact[i];
    }

    std::cout << ans << std::endl;

    return 0;
}

已经没有什么好害怕的了

首先我们可以将题目要求的转化为恰好存在 k 对满足 ai>bi

首先将序列 a,b 均按升序排列,设 hi,j 表示只考虑前 i 位,选出 j 对满足 ai>bi 的方案数。那么我们有:

hi,j=hi1,j+(ri(j1))hi1,j1

其中 ri=maxkibk<aik。不难发现 ri1ri,因此使用一个指针维护即可。

发现上述 DP 求出的方案数与题意不符,考虑如何修正。由于上述 DP 求出的是至少存在 j 对的方案数,我们可以考虑使用二项式反演将其转化为恰好存在 j 对的方案数。设 f(k) 表示钦定存在 k 对满足 ai>bi 的方案数,g(k) 表示恰好存在 k 对满足 ai>bi 的方案数。那么我们有:

f(k)=i=kn(ik)g(i)

根据二项式反演,我们有:

g(k)=i=kn(ik)(1)ikf(i)

考虑如何求出 f(k),发现 f(k)hn,k 的区别是 hn,k 未考虑剩余的数对的方案数,因此我们有:

f(k)=hn,k×(nk)!

直接计算即可,复杂度 O(n2)

Code
#include <bits/stdc++.h>

typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;

namespace MODINT_WITH_FIXED_MOD {
    constexpr valueType MOD = 1e9 + 9;

    template<typename T1, typename T2, typename T3 = valueType>
    void Inc(T1 &a, T2 b, const T3 &mod = MOD) {
        a = a + b;

        if (a >= mod)
            a -= mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    void Dec(T1 &a, T2 b, const T3 &mod = MOD) {
        a = a - b;

        if (a < 0)
            a += mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 sum(T1 a, T2 b, const T3 &mod = MOD) {
        return a + b >= mod ? a + b - mod : a + b;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 sub(T1 a, T2 b, const T3 &mod = MOD) {
        return a - b < 0 ? a - b + mod : a - b;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 mul(T1 a, T2 b, const T3 &mod = MOD) {
        return (long long) a * b % mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    void Mul(T1 &a, T2 b, const T3 &mod = MOD) {
        a = (long long) a * b % mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 pow(T1 a, T2 b, const T3 &mod = MOD) {
        T1 result = 1;

        while (b > 0) {
            if (b & 1)
                Mul(result, a, mod);

            Mul(a, a, mod);
            b = b >> 1;
        }

        return result;
    }
}// namespace MODINT_WITH_FIXED_MOD

using namespace MODINT_WITH_FIXED_MOD;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    valueType N, K;

    std::cin >> N >> K;

    if ((N - K) & 1) {
        std::cout << 0 << std::endl;

        return 0;
    }

    ValueVector Fact(N + 1, 1), InvFact(N + 1, 1);

    for (valueType i = 1; i <= N; ++i)
        Fact[i] = mul(Fact[i - 1], i);

    InvFact[N] = pow(Fact[N], MOD - 2);

    for (valueType i = N - 1; i >= 0; --i)
        InvFact[i] = mul(InvFact[i + 1], i + 1);

    auto C = [&](valueType n, valueType m) -> valueType {
        if (n < 0 || m < 0 || n < m)
            return 0;

        return mul(Fact[n], mul(InvFact[m], InvFact[n - m]));
    };

    ValueMatrix F(N + 1, ValueVector(N + 1, 0));

    F[0][0] = 1;

    ValueVector A(N + 1, 0), B(N + 1, 0);

    for (valueType i = 1; i <= N; ++i)
        std::cin >> A[i];

    for (valueType i = 1; i <= N; ++i)
        std::cin >> B[i];

    std::sort(A.begin() + 1, A.end());
    std::sort(B.begin() + 1, B.end());

    std::swap(A, B);

    valueType pointer = 0;

    F[0][0] = 1;

    for (valueType i = 1; i <= N; ++i) {
        while (pointer < N && A[pointer + 1] < B[i])
            ++pointer;

        for (valueType j = 0; j <= N; ++j) {
            F[i][j] = F[i - 1][j];

            if (j == 0)
                continue;

            Inc(F[i][j], mul(pointer - j + 1, F[i - 1][j - 1]));
        }
    }

    K = K + (N - K) / 2;

    valueType ans = 0;

    for (valueType i = K; i <= N; ++i) {
        if ((i - K) & 1) {
            Dec(ans, mul(C(i, K), mul(F[N][i], Fact[N - i])));
        } else {
            Inc(ans, mul(C(i, K), mul(F[N][i], Fact[N - i])));
        }
    }

    std::cout << ans << std::endl;

    return 0;
}

[JLOI2016] 成绩比较

首先,我们将方案数分为两部分:

  • 选出 k 名被碾压的同学并分配每位同学每个学科的分数与 B 神的关系。
  • 对每个学科的分数进行排列。

对第一部分做出一点解释:将计算碾压同学的方案数和分配分数与 B 神的关系放到一起是因为一位同学各学科的分数与 B 神的关系决定了其是否被碾压,因此需要将其放到一起考虑。

下面考虑如何计数,首先是第一部分。若不考虑除我们钦定外的同学是否被碾压,那么我们钦定已经有 w 名同学被碾压,而其余 nw1 名同学是否被碾压是不确定的,因此我们有:

fw=i(nw1Ri1)

发现这样计数在这 nw1 名同学中可能存在同学被碾压,因此 fw 实质上表达的是钦定有 w 名同学被碾压的方案数,设 gw 表示恰好有 w 名同学被碾压的方案数,那么我们有:

fw=i=wn(iw)gi

根据二项式反演,我们有:

gw=i=wn(iw)(1)iwfi

因此我们可以在 O(nm) 的时间内求出 gk,完成第一部分的计数。

下面考虑第二部分,如何统计每个学科的分数排列方案数。首先由于各学科是独立的,因此我们只需要考虑如何求解单独一门学科的方案数即可。设 U 表示该门学科的最高分,r 表示 B 神在这门学科中的排名

由于分数的值域较大,不能直接计算,因此我们考虑枚举有多少种分数,设有 t 种分数,那么一种直观的想法是枚举 B 神的分数 i,进而可以确定两个分数段及其对应的人数,有 x 人的分数一个长度为 l 的区间内的方案数为 lx,因此上述计数方法对应的计算式为:

ft=iinr×(ni)r1

但是我们可以发现上述计数方法存在一个问题,即可能会计算到实际分数种类不足 t 的情况,因此我们不妨设 ft 表示给定 t 种分数的情况下赋分方案数,gt 表示恰好使用 t 种分数的赋分方案数,那么我们有:

ft=i=1t(ti)gi

根据二项式反演,我们有:

gt=i=1t(ti)(1)tifi

因此我们可以在 O(n2) 的时间内求出 gt,完成第二部分的计数。

总复杂度为 O(n2m)

Code
#include <bits/stdc++.h>

typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::map<valueType, valueType> ValueMap;
typedef std::vector<ValueMap> MapVector;

namespace MODINT_WITH_FIXED_MOD {
    constexpr valueType MOD = 1e9 + 7;

    template<typename T1, typename T2, typename T3 = valueType>
    void Inc(T1 &a, T2 b, const T3 &mod = MOD) {
        a = a + b;

        if (a >= mod)
            a -= mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    void Dec(T1 &a, T2 b, const T3 &mod = MOD) {
        a = a - b;

        if (a < 0)
            a += mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 sum(T1 a, T2 b, const T3 &mod = MOD) {
        return a + b >= mod ? a + b - mod : a + b;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 sub(T1 a, T2 b, const T3 &mod = MOD) {
        return a - b < 0 ? a - b + mod : a - b;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 mul(T1 a, T2 b, const T3 &mod = MOD) {
        return (long long) a * b % mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    void Mul(T1 &a, T2 b, const T3 &mod = MOD) {
        a = (long long) a * b % mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 pow(T1 a, T2 b, const T3 &mod = MOD) {
        if (a == 0)
            return b == 0 ? 1 : 0;

        T1 result = 1;

        while (b > 0) {
            if (b & 1)
                Mul(result, a, mod);

            Mul(a, a, mod);
            b = b >> 1;
        }

        return result;
    }
}// namespace MODINT_WITH_FIXED_MOD

using namespace MODINT_WITH_FIXED_MOD;

class BinomialCoefficient {
private:
    valueType N;
    ValueVector Fact, InvFact;

public:
    BinomialCoefficient() = default;

    BinomialCoefficient(valueType n) : N(n), Fact(N + 1, 1), InvFact(N + 1, 1) {
        for (valueType i = 1; i <= N; ++i)
            Fact[i] = mul(Fact[i - 1], i);

        InvFact[N] = pow(Fact[N], MOD - 2);

        for (valueType i = N - 1; i >= 0; --i)
            InvFact[i] = mul(InvFact[i + 1], i + 1);
    }

    valueType operator()(valueType n, valueType m) {
        if (n < 0 || m < 0 || n < m)
            return 0;

        if (m > N)
            throw std::out_of_range("BinomialCoefficient::operator() : m > N");

        if (n <= N)
            return mul(Fact[n], mul(InvFact[m], InvFact[n - m]));

        valueType result = 1;

        for (valueType i = 0; i < m; ++i)
            Mul(result, n - i);

        Mul(result, InvFact[m]);

        return result;
    }
};

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    valueType N, M, K;

    std::cin >> N >> M >> K;

    BinomialCoefficient C(N);

    ValueVector U(M + 1, 0), R(M + 1, 0);

    for (valueType i = 1; i <= M; ++i)
        std::cin >> U[i];

    for (valueType i = 1; i <= M; ++i)
        std::cin >> R[i];

    valueType ans = 1;

    {// part one
        ValueVector F(N + 1, 0);

        for (valueType i = 0; i < N; ++i) {
            valueType const remain = N - i - 1;

            F[i] = C(N - 1, i);

            for (valueType j = 1; j <= M; ++j)
                Mul(F[i], C(remain, R[j] - 1));
        }

        valueType sum = 0;

        for (valueType i = K; i <= N; ++i) {
            if ((i - K) & 1) {
                Dec(sum, mul(F[i], C(i, K)));
            } else {
                Inc(sum, mul(F[i], C(i, K)));
            }
        }

        Mul(ans, sum);
    }

    std::cerr << "ans = " << ans << std::endl;

    {// part two
        for (valueType m = 1; m <= M; ++m) {
            ValueVector F(N + 1, 0);

            for (valueType t = 1; t <= N; ++t) {
                for (valueType i = 1; i <= t; ++i)
                    Inc(F[t], mul(pow(i, N - R[m]), pow(t - i, R[m] - 1)));
            }

            valueType sum = 0;

            for (valueType i = 1; i <= N; ++i) {
                valueType S = 0;

                for (valueType j = 1; j <= i; ++j) {
                    if ((i - j) & 1) {
                        Dec(S, mul(F[j], C(i, j)));
                    } else {
                        Inc(S, mul(F[j], C(i, j)));
                    }
                }

                Inc(sum, mul(S, C(U[m], i)));
            }

            Mul(ans, sum);
        }
    }

    std::cout << ans << std::endl;

    return 0;
}

CF1228E Another Filling the Grid

fi,j 表示钦定共 i 行和共 j 列最小值不为 1 的方案数,gi,j 表示恰好共 i 行和共 j 列最小值不为 1 的方案数。那么我们有:

fi,j=k=in(ki)l=jn(lj)gk,l

根据二项式反演,我们有:

gi,j=k=in(ki)(1)ikl=jn(lj)(1)jkfk,l

考虑如何求解 fi,j 发现若其不合法那么其不能填 1,剩余的数可以任意填。同时其余的各自也可以任意填,因此我们有:

fi,j=(ni)(nj)(k1)s×kn2s

其中 s=i×n+j×ni×j,即被选中的各自个数。我们要求的是 g0,0,直接计算即可,复杂度为 O(n2logn)

Code
#include <bits/stdc++.h>

typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;

namespace MODINT_WITH_FIXED_MOD {
    constexpr valueType MOD = 1e9 + 7;

    template<typename T1, typename T2, typename T3 = valueType>
    void Inc(T1 &a, T2 b, const T3 &mod = MOD) {
        a = a + b;

        if (a >= mod)
            a -= mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    void Dec(T1 &a, T2 b, const T3 &mod = MOD) {
        a = a - b;

        if (a < 0)
            a += mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 sum(T1 a, T2 b, const T3 &mod = MOD) {
        return a + b >= mod ? a + b - mod : a + b;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 sub(T1 a, T2 b, const T3 &mod = MOD) {
        return a - b < 0 ? a - b + mod : a - b;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 mul(T1 a, T2 b, const T3 &mod = MOD) {
        return (long long) a * b % mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    void Mul(T1 &a, T2 b, const T3 &mod = MOD) {
        a = (long long) a * b % mod;
    }

    template<typename T1, typename T2, typename T3 = valueType>
    T1 pow(T1 a, T2 b, const T3 &mod = MOD) {
        T1 result = 1;

        while (b > 0) {
            if (b & 1)
                Mul(result, a, mod);

            Mul(a, a, mod);
            b = b >> 1;
        }

        return result;
    }
}// namespace MODINT_WITH_FIXED_MOD

using namespace MODINT_WITH_FIXED_MOD;

class BinomialCoefficient {
private:
    valueType N;
    ValueVector Fact, InvFact;

public:
    BinomialCoefficient() = default;

    BinomialCoefficient(valueType n) : N(n), Fact(N + 1, 1), InvFact(N + 1, 1) {
        for (valueType i = 1; i <= N; ++i)
            Fact[i] = mul(Fact[i - 1], i);

        InvFact[N] = pow(Fact[N], MOD - 2);

        for (valueType i = N - 1; i >= 0; --i)
            InvFact[i] = mul(InvFact[i + 1], i + 1);
    }

    valueType operator()(valueType n, valueType m) {
        if (n < 0 || m < 0 || n < m)
            return 0;

        if (m > N)
            throw std::out_of_range("BinomialCoefficient::operator() : m > N");

        if (n <= N)
            return mul(Fact[n], mul(InvFact[m], InvFact[n - m]));

        valueType result = 1;

        for (valueType i = 0; i < m; ++i)
            Mul(result, n - i);

        Mul(result, InvFact[m]);

        return result;
    }
};

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    valueType N, K;

    std::cin >> N >> K;

    if (K == 1) {
        std::cout << 1 << std::endl;

        return 0;
    }

    BinomialCoefficient C(N);

    ValueMatrix F(N + 1, ValueVector(N + 1, 0));

    for (valueType i = 0; i <= N; ++i) {
        for (valueType j = 0; j <= N; ++j) {
            valueType const count = i * N + j * N - i * j;

            F[i][j] = 1;

            Mul(F[i][j], C(N, i));
            Mul(F[i][j], C(N, j));
            Mul(F[i][j], pow(K - 1, count));
            Mul(F[i][j], pow(K, N * N - count));
        }
    }

    valueType ans = 0;

    for (valueType i = 0; i <= N; ++i) {
        for (valueType j = 0; j <= N; ++j) {
            if ((i + j) & 1) {
                Dec(ans, F[i][j]);
            } else {
                Inc(ans, F[i][j]);
            }
        }
    }

    std::cout << ans << std::endl;

    return 0;
}

CF997C Sky Full of Stars

考虑求出没有任何一行或一列颜色相同的方案数,使用总方案数减去其就是答案。

fi,j 表示钦定共 i 行和共 j 列颜色相同的方案数,gi,j 表示恰好共 i 行和共 j 列颜色相同的方案数。那么我们有:

fi,j=k=in(ki)l=jn(lj)gk,l

根据二项式反演,我们有:

gi,j=k=in(ki)(1)ikl=jn(lj)(1)jkfk,l

而我们要求的是 g0,0,即:

g0,0=i=0n(i0)(1)ij=0m(1)mj(mj)fi,j=i=0nj=0n(1)i+jfi,j

考虑如何计算 fi,j,发现需要考虑的是选择的格子的颜色方案数,不难发现若 i>0j>0,由于行和列相互限制,那么被选择的格子颜色一定全部相同,因此我们可以按 i,j 是否等于 0 分类讨论,那么我们有:

  • i=0j=0,那么我们有 f0,0=3n2
  • i=0j>0i>0j=0,那么我们有 fi,0=f0,i=(ni)×3i×3n(ni),这是因为被选择的 i 行 / 列颜色各自独立,剩余的格子颜色可以任意填。
  • i>0j>0,那么我们有 fi,j=(ni)(nj)×3×3(ni)(nj)

依次考虑其对答案的贡献,对于前两种情况可以直接分别在 O(1)O(n) 的时间内计算,下面主要考虑如何计算第三种情况。

发现我们要求的实际上为:

i=1nj=1n(1)i+j×(ni)(nj)×3×3(ni)(nj)=3×i=1n(1)i×(ni)×j=1n(nj)(1)j×3ninj=3×i=1n(1)i×(ni)×(j=0n(nj)(1)j×3ninj3nin)=3×i=1n(1)i×(ni)×((3ni1)n3nin)

其可以在 O(nlogn) 的时间内计算,总复杂度为 O(n2logn)

Code
#include <bits/stdc++.h>

typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;

namespace MODINT_WITH_FIXED_MOD {
    constexpr valueType MOD = 998244353;

    template<typename T1, typename T2>
    void Inc(T1 &a, T2 b) {
        a = a + b;

        if (a >= MOD)
            a -= MOD;
    }

    template<typename T1, typename T2>
    void Dec(T1 &a, T2 b) {
        a = a - b;

        if (a < 0)
            a += MOD;
    }

    template<typename T1, typename T2>
    T1 sum(T1 a, T2 b) {
        return a + b >= MOD ? a + b - MOD : a + b;
    }

    template<typename T1, typename T2>
    T1 sub(T1 a, T2 b) {
        return a - b < 0 ? a - b + MOD : a - b;
    }

    template<typename T1, typename T2>
    T1 mul(T1 a, T2 b) {
        return (long long) a * b % MOD;
    }

    template<typename T1, typename T2>
    void Mul(T1 &a, T2 b) {
        a = (long long) a * b % MOD;
    }

    template<typename T1, typename T2>
    T1 pow(T1 a, T2 b) {
        T1 result = 1;

        while (b > 0) {
            if (b & 1)
                Mul(result, a);

            Mul(a, a);
            b = b >> 1;
        }

        return result;
    }
}// namespace MODINT_WITH_FIXED_MOD

using namespace MODINT_WITH_FIXED_MOD;

class BinomialCoefficient {
private:
    valueType N;
    ValueVector Fact, InvFact;

public:
    BinomialCoefficient() = default;

    BinomialCoefficient(valueType n) : N(n), Fact(N + 1, 1), InvFact(N + 1, 1) {
        for (valueType i = 1; i <= N; ++i)
            Fact[i] = mul(Fact[i - 1], i);

        InvFact[N] = pow(Fact[N], MOD - 2);

        for (valueType i = N - 1; i >= 0; --i)
            InvFact[i] = mul(InvFact[i + 1], i + 1);
    }

    valueType operator()(valueType n, valueType m) {
        if (n < 0 || m < 0 || n < m)
            return 0;

        if (m > N)
            throw std::out_of_range("BinomialCoefficient::operator() : m > N");

        if (n <= N)
            return mul(Fact[n], mul(InvFact[m], InvFact[n - m]));

        valueType result = 1;

        for (valueType i = 0; i < m; ++i)
            Mul(result, n - i);

        Mul(result, InvFact[m]);

        return result;
    }
};

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    valueType N;

    std::cin >> N;

    BinomialCoefficient C(N);

    valueType ans = 0;

    Inc(ans, pow(3, N * N));

    for (valueType i = 1; i <= N; ++i) {
        if (i & 1) {
            Dec(ans, mul(mul(2, C(N, i)), pow(3, N * (N - i) + i)));
        } else {
            Inc(ans, mul(mul(2, C(N, i)), pow(3, N * (N - i) + i)));
        }
    }

    for (valueType i = 1; i <= N; ++i) {
        if (i & 1) {
            Dec(ans, mul(mul(3, C(N, i)), sub(pow(sub(pow(3, N - i), 1), N), pow(3, N * (N - i)))));
        } else {
            Inc(ans, mul(mul(3, C(N, i)), sub(pow(sub(pow(3, N - i), 1), N), pow(3, N * (N - i)))));
        }
    }

    ans = sub(pow(3, N * N), ans);

    std::cout << ans << std::endl;

    return 0;
}
posted @   User-Unauthorized  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示