10.25 棋子
题意
给定一个 \(N\times M\) 的棋盘,有无数个棋子,请求出有多少种放棋子的方案,使得棋盘的每一行每一列上都至少有一个棋子
解法
考试时只想到了 \(O(N^2)\) 的解法。。只要容斥一下就可以 \(O(N)\) 了
先把两个限制条件化成一个:先保证每一列上至少有一个棋子
那么初始答案即为 \((2^N-1)^M\)
接下来容斥去掉这些方案中有空行的方案数
如何求得至少有一个空行的方案数?
可以发现这个容斥系数就是经典的 \(1,-1\) 交替的形式:因为在空行数为 \(1\) 的容斥过程中,空行数为 \(k\) 的状态将会被计算 \(k\) 次,又联系到至少的定义,就应该要联想到韦恩图了
代码
#include <cstdio>
#include <cstring>
using namespace std;
const int MAX_N = 1e6 + 10;
const int mod = 998244353;
int N, M;
int fac[MAX_N], Ifac[MAX_N], pw[MAX_N];
char s[MAX_N];
inline int mul(int x, int y) { return 1LL * x * y % mod; }
inline void inc(int& x, int y) { (x += y) >= mod ? x -= mod : 0; }
int fsp(int x, int y = mod - 2) {
int res = 1;
for (; y; x = mul(x, x), y >>= 1) if (y & 1) res = mul(res, x);
return res;
}
void init() {
int lim = 1e6;
fac[0] = 1;
for (int i = 1; i <= lim; ++i) fac[i] = mul(fac[i - 1], i);
Ifac[lim] = fsp(fac[lim]);
for (int i = lim - 1; i >= 0; --i) Ifac[i] = mul(Ifac[i + 1], i + 1);
pw[0] = 1;
for (int i = 1; i <= lim; ++i) pw[i] = mul(pw[i - 1], 2);
}
void modify(int& x) {
scanf("%s", s + 1);
int tmp = x;
for (int i = 1; i <= tmp; ++i) if (s[i] == '.') --x;
}
int C(int n, int m) { return mul(fac[n], mul(Ifac[m], Ifac[n - m])); }
int main() {
// freopen("box.in", "r", stdin);
// freopen("box.out", "w", stdout);
init();
scanf("%d%d", &N, &M);
modify(N);
modify(M);
int ans = fsp((pw[N] - 1 + mod) % mod, M);
int sign = -1;
for (int i = 1; i <= N; ++i) {
int ad = mul(fsp((pw[N - i] - 1 + mod) % mod, M), C(N, i));
ad *= sign;
inc(ans, (mod + ad) % mod);
sign *= -1;
}
printf("%d\n", ans);
return 0;
}