题目描述
难度分:2600
输入h、w(1≤h,w≤3600)和n(0≤n≤2400)。
有一个h行w列的棋盘,上面已经放了n个骨牌。一个骨牌由两个1×2大小的相邻方块组成。
然后输入n行,每行4个数,表示每个骨牌的第一个方块的行号和列号,以及第二个方块的行号和列号。
行列编号从1开始。
定义完美平衡棋盘:对于每一行和每一列,要么没有方块,要么有一个方块,要么有两个方块且这两个方块属于同一个骨牌。保证输入的棋盘是完美平衡棋盘。
你需要继续往棋盘上放零块或更多的骨牌,使得棋盘仍然是完美平衡的。输出方案数,模998244353。
输入样例1
5 7 2
3 1 3 2
4 4 4 5
输出样例1
8
输入样例2
5 4 2
1 2 2 2
4 3 4 4
输出样例2
1
输入样例3
23 42 0
输出样例3
102848351
算法
真难,做不了一点!
计数DP
先决定横着的骨牌放哪些相邻的列,竖的骨牌放哪些相邻的行。把这些位置剔除掉之后又按这个套路,决定横的骨牌放哪些列,竖的骨牌放哪些行。用visx和visy两个布尔数组来标记,visx[i]=true
表示第i行有骨牌,visy[j]=true
表示第j列有骨牌。restx和resty表示摆放完已有的n个骨牌之后,还剩下的行数和列数。
状态定义
f[i][j]表示前i行选j个相邻行放骨牌的方案数,g[i][j]表示前i列选j和相邻列放骨牌的方案数。
状态转移
以f的状态转移来进行说明,在第i行有两种选择:
- 第i行不放骨牌,那么相邻的j行就是之前i−1行放出来的,即f[i−1][j]。
- 否则放一个竖着的骨牌,就有j−1个相邻行是前i−2行放出来的,把f[i−2][j−1]累加到f[i][j]上。
g[i][j]也是类似分析,但是放横着的骨牌。综上,最终的答案就是
Σ⌊restx2⌋i=0Σ⌊resty2⌋j=0f[h][i]×g[w][j]×C(resty−2j,i)×C(restx−2i,j)×i!×j!
其中C(n,m)表示组合数,从n个中选择m个出来。减去两倍的i和j是因为放一个横放的骨牌相当于占掉一行两列,放一个竖放的骨牌相当于占掉一列两行。
复杂度分析
时间复杂度
求f的DP
值时间复杂度为O(h2);求g的DP
值时间复杂度为O(w2);最后求总答案需要遍历矩阵,时间复杂度为O(hw)。因此,整个算法的时间复杂度为O(h2+w2+hw)。
空间复杂度
空间消耗主要在于f和g两个DP
矩阵,空间复杂度为O(h2+w2)。求组合数时需要求逆元,但是空间是线性的。因此,整个算法的额外空间复杂度为O(h2+w2)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 3605;
const int MOD = 998244353;
int h, w, n;
bool visx[N], visy[N];
LL f[N][N], g[N][N], inv[N], finv[N], fi[N];
// 预处理逆元
void get_inv(int n) {
inv[0] = inv[1] = 1;
for(int i = 2; i <= n; i++) {
inv[i] = (MOD - MOD/i) * inv[MOD % i] % MOD;
}
finv[0] = finv[1] = fi[0] = fi[1] = 1;
for(int i = 2; i <= n; i++) {
fi[i] = fi[i - 1] * i % MOD;
finv[i] = finv[i - 1] * inv[i] % MOD;
}
}
// 排列数
LL A(LL n, LL m) {
if(n < m) return 0;
return fi[n] * finv[n - m] % MOD;
}
int main() {
get_inv(N - 5);
scanf("%d%d%d", &h, &w, &n);
int restx = 0, resty = 0;
for(int i = 1; i <= n; i++) {
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
visx[a] = visx[c] = visy[b] = visy[d] = true;
}
for(int i = 1; i <= h; i++) {
restx += 1 - visx[i];
}
for(int i = 1; i <= w; i++) {
resty += 1 - visy[i];
}
f[0][0] = g[0][0] = 1;
for(int i = 1; i <= h; i++) {
for(int j = 0; j*2 <= restx; j++) {
f[i][j] = (f[i - 1][j] + ((j && i >= 2 && !visx[i] && !visx[i - 1])? f[i - 2][j - 1]: 0)) % MOD;
}
}
for(int i = 1; i <= w; i++) {
for(int j = 0; j*2 <= resty; j++) {
g[i][j] = (g[i - 1][j] + ((j && i >= 2 && !visy[i] && !visy[i - 1])? g[i - 2][j - 1]: 0)) % MOD;
}
}
LL ans = 0;
for(int i = 0; i*2 <= restx; i++) {
for(int j = 0; j*2 <= resty; j++) {
ans = (ans + f[h][i] * g[w][j] % MOD * A(restx - 2*i, j) % MOD * A(resty - 2*j, i)) % MOD;
}
}
printf("%lld\n", ans);
return 0;
}