Matrix HDU - 6314(推式子好题)
题目大意:
\(Samwell·Tarly\) 正在学习绘制魔法矩阵,以保护自己免受白人步行者的伤害。
魔法矩阵是一个有 \(n\) 行和 \(m\) 列的矩阵,每个格子都应该涂成黑色或白色。
Sam想知道有多少种方法来绘制矩阵,满足于最终矩阵至少有 \(A\) 行和 \(B\) 列被绘制为完全黑色。因为答案可能太大,您只需要对 \(998244353\) 取模。
分析:
一道不错的推式子的好题。
首先,设 \(F[x][y]\) 为在大小为 \(x \times y\) 的矩阵中,乱涂色,但保证每一行,每一列都不完全为黑色的数量。根据容斥原理,容易得到:
\[F[x][y] = \sum_{i = 0}^x\sum_{j = 0}^y{x\choose i} * {y \choose j} * {(-1)}^{i+j} * 2^{(n - i) * (m-j)}
\]
假如我们求得了 \(F[x][y]\),那么答案 \(ans\) 就能很容易地求出来,即:
\[ans=\sum_{i=A}^n\sum_{j=B}^m{n \choose i}*{m\choose j} * F[n-i][m-j]
\]
然而直接求 \(F[x][y]\) 的时间复杂度为 \(O(n^2*m^2)\),无疑是无法接受的,因此考虑将 \(F\) 的表达式代入,得到:
\[ans=\sum_{i=A}^n\sum_{j=B}^m\sum_{p = 0}^{n-i}\sum_{q = 0}^{m-j}{n \choose i}*{m\choose j} * {{n-i}\choose p} * {{m-j} \choose q} * {(-1)}^{p+q} * 2^{(n - p) * (m-q)}
\]
移项,得:
\[ans=\sum_{i=A}^n\sum_{j=B}^m\sum_{p = 0}^{n-i}\sum_{q = 0}^{m-j}((-1)^p * {n \choose i}*{{n-i}\choose p}) *({m\choose j} * {{m-j} \choose q} * {(-1)}^{q}) * 2^{(n - i-p) * (m-j-q)}
\]
设 \(t1 = i + p\),\(t2 = j + q\),代入得:
\[ans=\sum_{t1=A}^n\sum_{t2=B}^m2^{(n - t1) * (m-t2)}\sum_{i = A}^{t1}\sum_{j = B}^{t2}((-1)^{t1-i} * {n \choose i}*{{n-i}\choose t1-i}) *({m\choose j} * {{m-j} \choose t2-j} * {(-1)}^{t2-j})
\]
即:
\[ans=\sum_{t1=A}^n\sum_{t2=B}^m2^{(n - t1) * (m-t2)}\sum_{i = A}^{t1}((-1)^{t1-i} * {n \choose i}*{{n-i}\choose t1-i}) *\sum_{j = B}^{t2}({m\choose j} * {{m-j} \choose t2-j} * {(-1)}^{t2-j})
\]
记 \(Fa[t]=\sum_{i = A}^{t}((-1)^{t-i} * {n \choose i}*{{n-i}\choose t-i})\),\(Fb[t] = \sum_{j = B}^{t}({m\choose j} * {{m-j} \choose t-j} * {(-1)}^{t-j})\)
预处理时间复杂度分别为 \(O(n^2)\) 和 \(O(m^2)\)。
则有:
\[ans=\sum_{t1=A}^n\sum_{t2=B}^m2^{(n - t1) * (m-t2)}Fa[t1] *Fb[t2]
\]
Code:
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN = 3e3;
const int MOD = 998244353;
int fa[MAXN + 5],fb[MAXN + 5],dp[MAXN + 5][MAXN + 5],p[30000005],n,m,a,b;
inline int qpow(int a,int m){
int ret = 1;
while(m){
if(m % 2 == 1){
ret *= a % MOD;
ret %= MOD;
}
m /= 2;
a = a * a % MOD;
}
return ret;
}
void pre(){
p[0] = 1;
for(int i = 1; i <= 3e6; i++){
p[i] = p[i - 1] * 2;
p[i] %= MOD;
}
}
signed main(){
dp[0][0] = 1;
for(int i = 1;i <= MAXN; i++){
for(int j = 0; j <= i; j++){
if(j == 0 || j == i)dp[i][j] = 1;
else dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1];
}
}
pre();
while(scanf("%lld%lld%lld%lld",&n,&m,&a,&b) == 4){
memset(fa,0,sizeof fa);
memset(fb,0,sizeof fb);
for(int t = a; t <= n; t ++){
for(int i = a; i <= t; i++){
fa[t] += dp[n][i] * dp[n - i][t - i] * ((t - i) % 2 ? -1 : 1);
}
}
for(int t = b; t <= m; t ++){
for(int i = b; i <= t; i ++){
fb[t] += dp[m][i] * dp[m - i][t - i] * ((t - i) % 2 ? -1 : 1);
}
}
int ans = 0;
for(int t1 = a; t1 <= n; t1++){
for(int t2 = b; t2 <= m; t2++){
ans += p[(n - t1) * (m - t2)] * (long long)(fa[t1] * fb[t2]) % MOD;
ans %= MOD;
}
}
cout << ans << "\n";
}
}