Live2D

Solution -「AGC 039F」Min Product Sum

Description

  Link.

  对于所有 n×m 的矩阵 A, aij[1,k]N, 求

Ai,jmin{mint=1m{ait},mint=1n{atj}}modP.

  n,m,k100, P 为素数.

Solution

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

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

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

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

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

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

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

  • j 个位置在填 A 值. 方案数为 (di+1)j(di)j (容斥使得至少有一个 i, 限定新列上取出 A 的列最大值为 i).
  • nj 个位置在填 B 值. 方案数为 inj.

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

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 @   Rainybunny  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2021-07-20 Solution -「UNR #5」「UOJ #671」诡异操作
2021-07-20 Solution -「多校联训」区间
点击右上角即可分享
微信分享提示