P5857 「SWTR-03」Matrix
一道不錯的組合計數題。
題目大意
有一個 \(n\times m\) 的 \(01\) 矩陣,初始時每個元素均為 \(0\)。 有 \(k\) 次操作,每次選擇第 \(x\) 行和第 \(y\) 列,將整行元素取反,再將整列元素取反。求 \(k\) 次操作後不同矩陣的種類數,答案對 \(998244353\) 取模。
大體思路
很容易發現,其實行與列的操作是互相獨立的,因此本題相當於可重複地取反 \(k\) 行,並且可重複地取反 \(k\) 列。
由於最終某一行(列)有貢獻,當且僅當這一行(列)被操作了奇數次,所以我們列舉有 \(i\) 行 \(j\) 列被操作了奇數次,其中 \(i\in [0,\min(n,k)]\)
但是,我們會發現樣例中 \(m=n=2\) 的情況便出現了問題。我們思考某一個元素變成 \(1\) 的條件。
如果用 \(0,1\) 表示某一行 \(x\) 被操作偶數次或奇數次,記為 \(I_x\),那麼 \(a_{x,y}=I_x\ \text{xor}\ I_y\)
我們研究何時這種情況會出現。基於行列情況等價,我們討論行的情況。由於 \(i\) 的奇偶性與 \(k\) 相同,即 \(i\equiv k\pmod 2\),同時取反後 \(n-i\equiv k\pmod 2\),兩式相加得 \(n\equiv 2k\equiv 0\pmod 2\)。也就是說,\(n,m\) 均為偶數。
同時,重複部分需要滿足 \(n-i\le k,i\ge n-k\)
由於重複部分在 \(A,D\) 中均被計算兩次,最終答案減去重複部分的一半即可,即 \(ans=A-\dfrac D 2\)。
由於 \(998244353\) 為質數,計算 \(\binom n m=\dfrac {n!}{m!(n-m)!}\) 以及使用除法時,利用費馬小定理 \(a^{p-1}\equiv 1\),\(a^{p-2}\equiv a^{-1}\pmod p\) 計算逆元即可。由於 \(n,m\) 同階,總的時間複雜度為 \(O(n\log p)\),其中 \(\log p\) 來自於預處理階乘逆元時快速冪的複雜度。
完整程式碼
#include <bits/stdc++.h>
using namespace std;
#define rep(ii,aa,bb) for(re ll ii = aa; ii <= bb; ii++)
#define Rep(ii,aa,bb) for(re int ii = aa; ii >= bb; ii--)
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair<int, int> PII;
const int maxn = 2e5 + 5;
const ll mod = 998244353;
namespace IO_ReadWrite {
#define re register
#define gg (p1 == p2 && (p2 = (p1 = _buf) + fread(_buf, 1, 1<<21, stdin), p1 == p2) ? EOF :*p1++)
char _buf[1<<21], *p1 = _buf, *p2 = _buf;
template <typename T>
inline void read(T &x){
x = 0; re T f=1; re char c = gg;
while(c > 57 || c < 48){if(c == '-') f = -1;c = gg;}
while(c >= 48 &&c <= 57){x = (x<<1) + (x<<3) + (c^48);c = gg;}
x *= f;return;
}
inline void ReadChar(char &c){
c = gg;
while(!isalpha(c)) c = gg;
}
template <typename T>
inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x/10);
putchar('0' + x % 10);
}
template <typename T>
inline void writeln(T x){write(x); putchar('\n');}
}
using namespace IO_ReadWrite;
ll n, m, k, T, inv[maxn], fac[maxn];
inline ll Pow(ll a, ll b) {
ll res = 1;
while(b) {
if(b & 1) res = (res * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return res % mod;
}
inline void init() {
int N = 2e5;
fac[0] = inv[0] = 1;
rep(i, 1, N) {
fac[i] = (i * fac[i - 1]) % mod;
inv[i] = Pow(fac[i], mod - 2);
}
}
inline ll C(ll n, ll m) {
ll res = fac[n];
return (((res * inv[m]) % mod) * inv[n - m]) % mod;
}
inline void solve() {
read(n); read(m); read(k);
ll c = (k & 1);
ll sI = 0, sJ = 0;
for(ll i = c; i <= min(n, k); i += 2) (sI += C(n, i)) %= mod;
for(ll j = c; j <= min(m, k); j += 2) (sJ += C(m, j)) %= mod;
ll ans = (sI * sJ) % mod;
if(!(n & 1) && !(m & 1)) {
ll dI = 0, dJ = 0;
for(ll i = max(n - k, 0ll); i <= min(n, k); i += 2) (dI += C(n, i)) %= mod;
for(ll j = max(m - k, 0ll); j <= min(m, k); j += 2) (dJ += C(m, j)) %= mod;
ll dlt = ((dI * dJ % mod) * inv[2]) % mod;
ans = (ans - dlt + mod) % mod;
}
writeln(ans);
}
int main () {
init();
read(T);
while(T --) solve();
return 0;
}