[AGC057E] RowCol/ColRow Sort(转化条件+dp)
问题的操作是对序列排序,关注的是数之间的大小关系,这时候我们可以考虑将题目的范围缩小,思考值域在 \([0,1]\) 怎么做?
手玩后发现,在这样的条件下,第一种操作即按照每行 \(0\) 的数量将行从上到下降序排序,列同理,为按照每列 \(0\) 的数量将行从上到下降序排序。
思考如何刻画 \(A\) 矩阵满足两种操作的条件。以行为例,设 \(r_i\) 表示第 \(i\) 行 \(0\) 的数量,那么 \(A\) 矩阵所有行的 \(r\) 构成可重集合与 \(B\) 矩阵的 \(r\) 构成的可重集合相等是必要的,同时根据 \(B\) 矩阵的性质,可以得出这也是充分的。
那么刻画就是行列的可重集合与 \(B\) 相同。根据 \(B\) 矩阵的特殊性质,满足条件的 \(A\) 矩阵一定对应两个可重集合排列后唯一一种方案(每个排列并不对应唯一一种矩阵),那么 \([0,1]\) 的答案就是两个可重排列数相乘。
回到原问题,只需要将前面的方法拓展,枚举 \(k\in[0,9)\),令 \(A_{i,j}\leftarrow[A_{i,j}\le k]\),\(B_{i,j}\leftarrow[A_{i,j}\le k]\),那么一个合法的 \(A\) 矩阵一定在所有 \(k\) 上都满足上面的条件。但是方案显然不是简单的排列数相乘了,那么问题就聚焦在排列满足的条件上。
注意到操作的特殊性,枚举每个 \(k\),我们其实可以这样刻画一个矩阵 \(A\):存在两个排列 \(p^{k}(1\cdots n)\) 和 \(q^{k}(1\cdots m)\),使得 \(A_{i,j}=B_{p^k_i,q^k_j}\)。一个合法的 \(A\) 对应唯一一个集合 \(\{{(p^0,q^0)\cdots (p^k, q^k)\}}\)。但是对于每一个 \(k+1\) 时,\(p^{k}\) 对 \(p^{k+1}\) 有制约,具体的说,\(B_{p^k_i,q^k_j}\le k\Rightarrow B_{p^{k+1}_i,q^{k+1}_j}\le k+1\)。这里有四个排列非常难受,但实际上我们只关心两对排列的相对关系,并且不关心其他排列,所以考虑逆置换,设 \(p^{k}_i=i\),那么条件就变成:\(B_{i,j}\le k\Rightarrow B_{p^{k}_i,q^{k}_j}\le k+1\) (这里可以当作是枚举 \(k\) 或 \(k+1\) 时满足的条件,看你怎么理解)。这样转化后单次计算 \(k\) 情况下的方案数就独立了。
继续改写条件。设 \(r_i=\sum_{j=1}^m[B_{i,j}\le k]\),\(c_{j}=\sum_{i=1}^n[A_{i,j}\le k+1]\),那么就有 \(j\le r_i\Rightarrow p^k_i\le c_{q^k_{j}}\),即为 \(p^k_i\le \min_{j\le r_i}c_{q^k_{j}}\),再根据 \(c\) 的单调性,即 \(p^k_i\le c_{\max_{j\le r_i} q^k_{j}}\)。
至此我们将原问题变为:给定两个单调不升的序列 \(r\) 和 \(c\),求满足上述的排列 \(p\) 和 \(q\) 的方案数。考虑用 dp 求解。设 \(f_{i,j}\) 表示 \(\max(q_1\cdots q_{i})=j\) 的方案数。可以发现 \(p\) 和 \(q\) 并不是相互制约,所以在求解过程中可以只考虑一个排列的方案数。先考虑 \(q\),转移只需要枚举当前位 \(q_i\) 是否为最大值。
怎么考虑 \(p\),可以在 dp 过程中用一个指针 \(t\) 从右往左扫描 \(r_i\),如果 \(r_t=i\),那么考虑 \(p_t\) 这一位的填法即可。
每一次 \(k\) 算出来的方案数都要除以 \(\prod_{k=1}^m(\sum_{i=1}^n[r_{i}=k])!\cdot\prod_{k=1}^m(\sum_{i=1}^n[r_{i}=k])!\),这是可重排列重复算的部分。
时间复杂度 \(O(km^2)\)。
#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back
#define mk std::make_pair
using u32 = unsigned int;
using u64 = unsigned long long;
using i64 = long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f;
const int N = 1.5e3 + 10, mod = 998244353;
int n, m;
int B[N][N];
i64 inv[N], fac[N], r[15][N], c[15][N], cnt[N];
i64 f[N][N], ans = 1;
i64 qpow(i64 a, i64 b) {
i64 ret = 1;
while(b) {
if(b & 1) ret = ret * a % mod;
a = a * a % mod;
b >>= 1;
}
return ret;
}
int main () {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> m;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
std::cin >> B[i][j];
r[B[i][j]][i]++;
c[B[i][j]][j]++;
}
}
int M = std::max(n, m);
fac[0] = 1;
for(i64 i = 1; i <= M; i++) fac[i] = fac[i - 1] * i % mod;
inv[M] = qpow(fac[M], mod - 2);
for(i64 i = M - 1; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % mod;
for(int k = 0; k < 9; k++) {
for(int j = 1; j <= n; j++) r[k + 1][j] += r[k][j];
for(int j = 1; j <= m; j++) c[k + 1][j] += c[k][j];
int t = n;
for(; t && !r[k][t]; t--);
memset(f, 0, sizeof(f));
f[0][0] = 1;
for(i64 i = 1; i <= m; i++) {
i64 s = f[i - 1][0];
for(i64 j = 1; j <= m; j++) {
f[i][j] = ((j - i + 1) * f[i - 1][j] + s) % mod;
s = (s + f[i - 1][j]) % mod;
}
for(; t && r[k][t] == i; t--) {
for(int j = 1; j <= m; j++) f[i][j] = f[i][j] * std::max(0LL, c[k + 1][j] - t + 1) % mod;
}
}
ans = ans * f[m][m] % mod;
memset(cnt, 0, sizeof(cnt));
for(int i = 1; i <= n; i++) cnt[r[k][i]]++;
for(int i = 1; i <= m; i++) ans = ans * inv[cnt[i]] % mod;
memset(cnt, 0, sizeof(cnt));
for(int i = 1; i <= m; i++) cnt[c[k][i]]++;
for(int i = 0; i <= n; i++) ans = ans * inv[cnt[i]] % mod;
}
std::cout << ans << "\n";
return 0;
}

浙公网安备 33010602011771号