QOJ #3082. Ascending Matrix 题解

Description

给定整数 \(N, M, K, R, C, V\)。求满足以下所有条件的 \(N \times M\) 整数矩阵 \(a = (a_{i,j})\) 的数量,并对结果取模 \(998244353\)

  • 对所有 \(1 \leq i \leq N,\, 1 \leq j \leq M\),有 \(1 \leq a_{i,j} \leq K\)
  • 对所有 \(1 \leq i \leq N,\, 1 \leq j \leq M-1\),有 \(a_{i,j} \leq a_{i,j+1}\)
  • 对所有 \(1 \leq i \leq N-1,\, 1 \leq j \leq M\),有 \(a_{i,j} \leq a_{i+1,j}\)
  • 并且满足 \(a_{R,C} = V\)

\(1\leq N,M\leq 200,1\leq K\leq 100\)

Solution

(注:这里 \(R\)\(C\) 默认都先减了 \(1\)

首先这题不太好直接计数,考虑转格路后用 LGV 引理计数。

先找到数值 \(\leq i\)\(>i\) 的分界线 \(L_i\),这是个从 \((0,m)\)\((n,0)\) 的方向只有左下的路径。

需要满足 \(L_i\) 完全在 \(L_{i+1}\) 的左上,但是可以有交点重合,那么将 \(L_i\) 向右下平移 \(i-1\) 步后就不会有交点了。

也就是说第 \(i\) 个起点为 \((i-1,m+i-1)\),第 \(i\) 个终点为 \((n+i-1,i-1)\),要求这 \(k-1\) 个路径不能有交。

然后去满足 \(a_{r,c}=v\) 的限制。

这时需要满足前 \(v-1\) 条路径需要经过 \((r+v-2,c+v-2)\) 的左上角,且第 \(v\) 条路径需要经过 \((r+v,c+v)\) 的右下角。

如果对前 \(v-1\) 和后 \(k-v\) 条路径按照不同的方式计数的化就不能用 LGV 引理进行容斥了,考虑统一标准。

容易发现它们都不能经过 \((r+v-1,c+v-1)\),那么可以把 \((r+v-1,c+v-1)\) 删掉,问题变为满足不交限制之后要求经过 \((r+v-2,c+v-2)\) 左上角的路径需要有恰好 \(v-1\) 条。

\(a_{i,j}\) 表示从第 \(i\) 个起点走到第 \(j\) 个终点,且不经过 \((r+v-2,c+v-2)\) 左上角的方案数;\(b_{i,j}\) 表示从第 \(i\) 个起点走到第 \(j\) 个终点,且经过 \((r+v-2,c+v-2)\) 左上角的方案数。然后跑 \(w_{i,j}=a_{i,j}+b_{i,j}x\) 的行列式,要求出第 \(v-1\) 次系数。

插值维护即可。

时间复杂度:\(O(n^3+k^4)\)

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 205, kMaxK = 105, kMaxL = 305, kMod = 998244353;

int n, m, k, r, c, v;
int f[kMaxL][kMaxL][2], w[kMaxK][kMaxK][2], val[kMaxK], ww[kMaxK][kMaxK];

int qpow(int bs, int64_t idx = kMod - 2) {
  int ret = 1;
  for (; idx; idx >>= 1, bs = (int64_t)bs * bs % kMod)
    if (idx & 1)
      ret = (int64_t)ret * bs % kMod;
  return ret;
}

inline int add(int x, int y) { return (x + y >= kMod ? x + y - kMod : x + y); }
inline int sub(int x, int y) { return (x >= y ? x - y : x - y + kMod); }
inline void inc(int &x, int y) { (x += y) >= kMod ? x -= kMod : x; }
inline void dec(int &x, int y) { (x -= y) < 0 ? x += kMod : x; }

void get(int det) {
  memset(f, 0, sizeof(f));
  f[det][m + det][det <= r + v - 2 && m + det <= c + v - 2] = 1;
  for (int i = det; i <= n + k - 1; ++i) {
    for (int j = m + det; ~j; --j) {
      if (i == r + v - 1 && j == c + v - 1) continue;
      int op = (i <= r + v - 2 && j <= c + v - 2);
      if (i) {
        for (int lst = 0; lst <= 1; ++lst) inc(f[i][j][lst | op], f[i - 1][j][lst]);
      }
      for (int lst = 0; lst <= 1; ++lst) inc(f[i][j][lst | op], f[i][j + 1][lst]);
    }
  }
  for (int i = 0; i < k - 1; ++i) w[det + 1][i + 1][0] = f[n + i][i][0], w[det + 1][i + 1][1] = f[n + i][i][1];
}

int getdet(int n) {
  int ret = 1;
  for (int i = 1; i <= n; ++i) {
    if (!ww[i][i]) {
      for (int j = i + 1; j <= n; ++j) {
        if (ww[j][i]) {
          std::swap(ww[i], ww[j]), ret = sub(0, ret);
          break;
        }
      }
    }
    if (!ww[i][i]) return 0;
    ret = 1ll * ret * ww[i][i] % kMod;
    for (int j = i + 1; j <= n; ++j) {
      int d = 1ll * ww[j][i] * qpow(ww[i][i]) % kMod;
      for (int k = i; k <= n; ++k) dec(ww[j][k], 1ll * ww[i][k] * d % kMod);
    }
  }
  return ret;
}

int lagrange(int v) {
  int ret = 0;
  for (int i = 1; i <= k; ++i) {
    static int f[kMaxK];
    memset(f, 0, sizeof(f));
    f[0] = 1;
    int coef = val[i];
    for (int j = 1; j <= k; ++j) {
      if (i == j) continue;
      coef = 1ll * coef * qpow(sub(i, j)) % kMod;
      for (int s = k - 1; ~s; --s) {
        f[s] = 1ll * f[s] * (kMod - j) % kMod;
        if (s) inc(f[s], f[s - 1]);
      }
    }
    inc(ret, 1ll * coef * f[v] % kMod);
    // std::cerr << ret << ' ' << 1ll * coef * f[v] % kMod << '\n';
  }
  return ret;
}

void dickdreamer() {
  std::cin >> n >> m >> k >> r >> c >> v; --r, --c;
  for (int i = 0; i < k - 1; ++i) get(i);
  // for (int i = 1; i <= k - 1; ++i) {
  //   for (int j = 1; j <= k - 1; ++j) {
  //     std::cerr << w[i][j][0] << " \n"[j == k - 1];
  //   }
  // }
  // for (int i = 1; i <= k - 1; ++i) {
  //   for (int j = 1; j <= k - 1; ++j) {
  //     std::cerr << w[i][j][1] << " \n"[j == k - 1];
  //   }
  // }
  for (int i = 1; i <= k; ++i) {
    for (int x = 1; x <= k - 1; ++x)
      for (int y = 1; y <= k - 1; ++y)
        ww[x][y] = add(w[x][y][0], 1ll * i * w[x][y][1] % kMod);
    val[i] = getdet(k - 1);
  }
  std::cout << lagrange(v - 1) << '\n';
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}
posted @ 2025-08-26 16:13  下蛋爷  阅读(16)  评论(0)    收藏  举报