Live2D

Solution -「AGC 039F」Min Product Sum

\(\mathscr{Description}\)

  Link.

  对于所有 \(n\times m\) 的矩阵 \(A\), \(a_{ij}\in[1,k]\cap\mathbb N\), 求

\[\sum_A\prod_{i,j}\min\left\{\min_{t=1}^m\{a_{it}\},\min_{t=1}^n\{a_{tj}\}\right\}\bmod P. \]

  \(n,m,k\le100\), \(P\) 为素数.

\(\mathscr{Solution}\)

  很有想象力的状态设计, 不愧是 AGC. (

  (下令题面中 \(k\)\(d\), \(k\) 另作他用.) 第一步是技巧性的转化: 把所有 "权值" 构造成方案数, 将问题变为纯纯的方案数计数问题. 这里不难将 \(A\) 的权值描述为满足 \(1\le b_{ij}\le a_{ij}\) 的矩阵 \(B\) 的数量. 我们就是要求所有 \((A,B)\) 的方案.

  令 \(f(i,j,k)\) 表示考虑了所有 \(B\) 中最大值 \(\in[1,i]\) 行列以及 \(A\) 中所有最小值 \(\in[1,i]\) 的行列, 总共有 \(j\) 行被考虑到, \(k\) 列被考虑到时, 所有可确定位置的方案数之和. 注意, 对于每个位置, 当其第一次被行 (或列) 考虑到时, 我们先确定其 \(B\) 中的值; 当其接着被列 (或行) 考虑到时, 我们再确定其 \(A\) 中的值.

  转移不妨先填出最小值为 \(i\) 的行, 再填出最小值为 \(i\) 的列.

  填行, 对于新被考虑到的一行:

  • \(k\) 个位置在填 \(A\) 值. 因为本行最小值钦定为 \(i\), 所以方案数为 \((d-i+1)^k\).
  • \(m-k\) 个位置在填 \(B\) 值. 同理, 方案数为 \(i^{m-k}-(i-1)^{m-k}\) (容斥使得至少有一个 \(i\), 限定新行上取出 \(B\) 的行最大值为 \(i\)).

  填列, 对于新被考虑到的一列:

  • \(j\) 个位置在填 \(A\) 值. 方案数为 \((d-i+1)^j-(d-i)^j\) (容斥使得至少有一个 \(i\), 限定新列上取出 \(A\) 的列最大值为 \(i\)).
  • \(n-j\) 个位置在填 \(B\) 值. 方案数为 \(i^{n-j}\).

  两个不那么对称的容斥限定使得 "新建行列上有 [\(B\) 的最大值 \(i\) / \(A\) 的最小值 \(i\) / 二者]" 三种情况都能唯一地取出来. 那么枚举每次新建的行列数量, 可以做到 \(\mathcal O(nmk(n+m))\) 转移求解.

\(\mathscr{Code}\)

/*+Rainybunny+*/

#include <bits/stdc++.h>

#define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
#define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)

const int MAXN = 100;
int n, m, d, P, bino[MAXN + 5][MAXN + 5], f[2][MAXN + 5][MAXN + 5];

inline int mul(const int u, const int v) { return 1ll * u * v % P; }
inline void subeq(int& u, const int v) { (u -= v) < 0 && (u += P); }
inline int sub(int u, const int v) { return (u -= v) < 0 ? u + P : u; }
inline void addeq(int& u, const int v) { (u += v) >= P && (u -= P); }
inline int add(int u, const int v) { return (u += v) < P ? u : u - P; }
inline int mpow(int u, int v) {
    int ret = 1;
    for (; v; u = mul(u, u), v >>= 1) ret = mul(ret, v & 1 ? u : 1);
    return ret;
}

inline void init() {
    bino[0][0] = 1;
    rep (i, 1, MAXN) {
        bino[i][0] = 1;
        rep (j, 1, i) bino[i][j] = add(bino[i - 1][j - 1], bino[i - 1][j]);
    }
}

int main() {
    scanf("%d %d %d %d", &n, &m, &d, &P);
    init();

    f[0][0][0] = 1;
    rep (i, 1, d) {
        auto fcur = f[0], fnex = f[1];
        rep (j, 0, n) rep (k, 0, m) {
            if (int& cur = fcur[j][k]) {
                int pw = 1, c = mul(sub(mpow(i, m - k),
                  mpow(i - 1, m - k)), mpow(d - i + 1, k));
                rep (l, 0, n - j) {
                    addeq(fnex[j + l][k], mul(mul(bino[repl][l], pw), cur));
                    pw = mul(pw, c);
                }
                cur = 0;
            }
        }
        
        fcur = f[1], fnex = f[0];
        rep (j, 0, n) rep (k, 0, m) {
            if (int& cur = fcur[j][k]) {
                int pw = 1, c = mul(sub(mpow(d - i + 1, j),
                  mpow(d - i, j)), mpow(i, n - j));
                rep (l, 0, m - k) {
                    addeq(fnex[j][k + l], mul(mul(bino[repl][l], pw), cur));
                    pw = mul(pw, c);
                }
                cur = 0;
            }
        }
    }
    printf("%d\n", f[0][n][m]);
    return 0;
}

posted @ 2022-07-20 22:05  Rainybunny  阅读(48)  评论(0编辑  收藏  举报