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;
}

猜你喜欢

转载自www.cnblogs.com/VeniVidiVici/p/11740918.html