网格染色 [组合计数]

网 格 染 色 网格染色

N × M N \times M N×M 的网格图, 给任意多个网格染色, 计算 使得每行每列至少有一个染色网格 的 染色方案数 .

N , M ≤ 1 0 6 N, M \le 10^6 N,M106


正 解 部 分 \color{red}{正解部分}

答案 = = = 都合法的总方案数 − - 合法前提下 不合法的方案数 .

都合法的总方案数 = = = ( 2 n − 1 ) m (2^n - 1)^m (2n1)m, 不合法的方案数 = ∑ i = 1 N ( − 1 ) i ( n i ) ( 2 n − i − 1 ) m = \sum\limits_{i=1}^N (-1)^i \begin{pmatrix} n \\ i \end{pmatrix}(2^{n-i} - 1)^m =i=1N(1)i(ni)(2ni1)m .

∴ a n s = ∑ i = 0 N ( − 1 ) i ( n i ) ( 2 n − i − 1 ) m \therefore ans = \sum\limits_{i=0}^N (-1)^i \begin{pmatrix} n \\ i \end{pmatrix}(2^{n-i} - 1)^m ans=i=0N(1)i(ni)(2ni1)m .


实 现 部 分 \color{red}{实现部分}

#include
#define reg register

const int maxn = 1e6 + 5;
const int mod = 998244353;

int N;
int M;
int pw[maxn];
int inv[maxn];
int fac[maxn];
int ifac[maxn];

char S[maxn];

int C(int n, int m){ return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod; }

int Ksm(int a, int b){
        int s = 1;
        while(b){
                if(b & 1) s = 1ll*s*a % mod;
                a = 1ll*a*a % mod; b >>= 1;
        }
        return s;
}

int main(){
        scanf("%d%d", &N, &M); 
        scanf("%s", S+1); int t = N;
        for(reg int i = 1; i <= t; i ++) if(S[i] == '.') N --;
        scanf("%s", S+1); t = M;
        for(reg int i = 1; i <= t; i ++) if(S[i] == '.') M --;
        inv[1] = 1; for(reg int i = 2; i <= std::max(N, M); i ++) inv[i] = ((-1ll*mod/i*inv[mod%i])%mod + mod) % mod;
        fac[0] = 1; for(reg int i = 1; i <= std::max(N, M); i ++) fac[i] = 1ll*fac[i-1]*i%mod;
        ifac[0] = 1; for(reg int i = 1; i <= std::max(N, M); i ++) ifac[i] = 1ll*ifac[i-1]*inv[i] % mod;
        pw[0] = 1; for(reg int i = 1; i <= std::max(N, M); i ++) pw[i] = 2ll*pw[i-1] % mod;
        int Ans = 0;
        for(reg int i = 0; i <= N; i ++){
                int add = 1ll*C(N, i)*Ksm(pw[N-i]-1, M)%mod;
                if(i & 1) add = -add;
                Ans = ((Ans + add)%mod + mod) % mod;
        }
        printf("%d\n", Ans);
        return 0;
}

你可能感兴趣的:(数学-计数问题,First)