感觉这题是一道细节比较多但是不难的题,配不上这个 *3100
的标签,连萌新都能独立做出来......
题面
给定一个 (n imes m) 的矩阵,要你把矩阵的每一个格子染成蓝的或黄的,要求每一行的蓝色格子段数(连通块数)恰好有一个,每一列的黄格子段数恰好有一个。求染色方案数,答案对 (998244353) 取模。
数据范围:(1 le n, m le 2000)。
题解
这里的 (n, m) 可能是反的,但是不影响答案(
观察蓝色的线段。
经过手膜大致可以发现可以分成两类情况讨论
第一类
蓝色的分成两个部分,上面一个下面一个。
这两个部分都是 "单峰" 的。
要求两个部分的 "高度" 之和恰好为 (m)。
考虑美剧第一个的高度和两个峰值的位置(枚举最左边的或最右边的即可)
[2 sumlimits_{z = 1}^{m - 1} sumlimits_{i = 1}^{n - 1} sumlimits_{j = i + 1}^{n} inom{i + z - 1}{z} inom{n - i + z - 1}{z - 1} inom{j + m - z - 2}{m - z - 1} inom{n - j + m - z}{m - z}
]
第二类
蓝色的分成两个部分,上面一个下面一个。
要求两个部分的 "高度" 之和 (> m)。
枚举高度,然后枚举这个交的部分(黑线)的 (x) 坐标即可。
[2 sumlimits_{p = 1}^{n} sumlimits_{i = 1}^{m - 1} sumlimits_{j = m - i + 1}^{m - 1} inom{p + i - 1}{i} inom{n - p + m - j - 1}{m - j - 1} inom{p + m - i - 1}{m - i - 1} inom{n - p + j - 1}{j}
]
现在的复杂度是 (Theta(n^3)) 的((n, m) 同阶)
但是这两个柿子是可以前缀和优化的,做到 (Theta(n^2))。
上面这些柿子是哪里来的?
考虑一个长为 (x) 的不严格单调下降序列,要求第一个值 (le y)。
这个是经典的插板法,先化成严格的,然后再插板,一个板代表这个序列的一个元素。那么答案就是 (inom{x+y}{x})。
那里要乘以 (2) 是因为我们钦定了上面和下面的左右关系。
你有没有感觉这两个柿子是一模一样的?
嗯是的,其实这两种情况只是 (n. m) 交换了一下,本质是差不多的(
代码
#include<bits/stdc++.h>
#define L(i, j, k) for(int i = j, i##E = k; i <= i##E; i++)
#define R(i, j, k) for(int i = j, i##E = k; i >= i##E; i--)
#define ll long long
#define pii pair<int, int>
#define db double
#define x first
#define y second
#define ull unsigned long long
#define sz(a) ((int) (a).size())
#define vi vector<int>
using namespace std;
const int N = 4e5 + 7, mod = 998244353;
int ns, n, m;
int fac[N], inv[N], ifac[N];
void init(int x) {
fac[0] = ifac[0] = inv[1] = 1;
L(i, 2, x) inv[i] = (ll) inv[mod % i] * (mod - mod / i) % mod;
L(i, 1, x) fac[i] = (ll) fac[i - 1] * i % mod, ifac[i] = (ll) ifac[i - 1] * inv[i] % mod;
}
int C(int x, int y) {
return x < y || y < 0 ? 0 : (ll) fac[x] * ifac[y] % mod * ifac[x - y] % mod;
}
int main() {
cin >> n >> m, init(max(n, m) * 4);
L(z, 1, m - 1) {
int now = 0;
R(r1, n - 1, 1) {
(now += (ll) C(r1 + m - z - 1, m - z - 1) * C(n - r1 + m - z - 1, m - z) % mod) %= mod;
(ns += (ll) C(r1 + z - 1, z) * C(n - r1 + z - 1, z - 1) % mod * now % mod) %= mod;
}
}
L(p, 1, n) {
int now = 0;
L(z1, 1, m - 1) {
(now += (ll) C(n - p + z1 - 2, z1 - 2) * C(n - p + m - z1, m - z1 + 1) % mod) %= mod;
(ns += (ll) C(p + z1 - 1, z1) * C(p + m - z1 - 1, m - z1 - 1) % mod * now % mod) %= mod;
}
}
ns = (ll) ns * 2 % mod;
cout << ns << "
";
return 0;
}
祝大家学习愉快!