10.25 棋子

题意

给定一个 (N imes 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
", ans);
		
	return 0;
}
原文地址:https://www.cnblogs.com/VeniVidiVici/p/11740918.html