「HDU 6314」Matrix「容斥」

题目传送门

题意

对一个 nm n ∗ m 的二维方格涂上黑白两色,求至少 a a 行全黑,b列全黑的方案数.

题解

可以先考虑行,然后考虑二维就十分简单了.

我们用 g(k) g ( k ) 表示至少有 k k 行全黑的方案数,容斥求一下:

g(k)=i=knf(i)Cni2ni

因为是至少,所以需要容斥, Cin C n i 表示 n n 行选i行, 2ni 2 n − i 表示其中剩下的行状态随意, f(k) f ( k ) 为容斥系数,我们需要递推出这个容斥系数.

每个行数为 j j 的答案计算贡献只能算一次,所以:i=kjf(i)Cji=1

ni=kf(i)Cin ∑ i = k n f ( i ) ∗ C n i

=n1i=kf(i)Cin+f(n) = ∑ i = k n − 1 f ( i ) ∗ C n i + f ( n )

=n1i=kf(i)(Ci1n1+Cin1)+f(n) = ∑ i = k n − 1 f ( i ) ∗ ( C n − 1 i − 1 + C n − 1 i ) + f ( n )

=n1i=k(f(i)Ci1n1+f(i)Cin1)+f(n) = ∑ i = k n − 1 ( f ( i ) ∗ C n − 1 i − 1 + f ( i ) ∗ C n − 1 i ) + f ( n )

=1+n1i=kf(i)Cin1+f(n) = 1 + ∑ i = k n − 1 f ( i ) ∗ C n − 1 i + f ( n )

因为 ni=kf(i)Cin=1 ∑ i = k n f ( i ) ∗ C n i = 1 ,因此 f(n)=n1i=kf(i)Ci1n1 f ( n ) = − ∑ i = k n − 1 f ( i ) ∗ C n − 1 i − 1

O(n2) O ( n 2 ) O(m2) O ( m 2 ) 的时间递推出行列的容斥系数 fa f a fb f b ,然后就可以得到答案的式子:

ans=ni=amj=bfa(i)Cinfb(j)Cjm2(ni)(mj) a n s = ∑ i = a n ∑ j = b m f a ( i ) ∗ C n i ∗ f b ( j ) ∗ C m j ∗ 2 ( n − i ) ∗ ( m − j )

#include <cstdio> 

const int MAXN = 3000;
const int MOD = 998244353;

int n, m, a, b;
int fa[MAXN + 10], fb[MAXN + 10];
int pow2[MAXN * MAXN + 10], c[MAXN + 10][MAXN + 10];

void init() {
    pow2[0] = 1;
    for(int i = 1; i <= MAXN * MAXN; i ++)
        pow2[i] = (pow2[i - 1] * 2LL) % MOD;
    for(int i = 0; i <= MAXN; i ++) {
        c[i][0] = c[i][i] = 1;
        for(int j = 1; j < i; j ++)
            c[i][j] = (1ll * c[i-1][j-1] + c[i-1][j]) % MOD;
    }
}

void calc_f() {
    fa[a] = 1;
    for(int i = a + 1; i <= n; i ++) {
        fa[i] = 0;
        for(int j = a; j < i; j ++)
            fa[i] = (fa[i] + 1ll * c[i-1][j-1] * fa[j] % MOD) % MOD;
        fa[i] = ((-fa[i]) % MOD + MOD) % MOD;
    }
    fb[b] = 1;
    for(int i = b + 1; i <= m; i ++) {
        fb[i] = 0;
        for(int j = b; j < i; j ++)
            fb[i] = (fb[i] + 1ll * c[i-1][j-1] * fb[j] % MOD) % MOD;
        fb[i] = ((-fb[i]) % MOD + MOD) % MOD;
    }
}

int main() {
    init();
    while(~ scanf("%d%d%d%d", &n, &m, &a, &b)) {
        calc_f();
        int ans = 0;
        for(int i = a; i <= n; i ++)
            for(int j = b; j <= m; j ++)
                ans = (ans + 1ll * fa[i] * c[n][i] % MOD * fb[j] % MOD * c[m][j] % MOD * pow2[(n - i) * (m - j)] % MOD) % MOD; 
        printf("%d\n", (ans % MOD + MOD) % MOD);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值