Cheerleaders UVA - 11806

Cheerleaders - UVA 11806 - Virtual Judge

题目大意:有一个n*m的网格,要把其中k个格子涂黑,且满足最上、下一行、最左、右一列分别至少有一格黑,问方案数有多少

2<=m,n<=50;k<=500

思路:因为合法的方案数不好考虑,所以考察不合法的方案数,我们设第一行没有黑格为A,第一列没有黑格为B,最后一行没有黑格为C,最后一列没有黑格为D,根据容斥原理有:非法方案数=-A-B-C-D+AB+AC+AD+BC+BD+CD-ABC-ABD-ACD-BCD+ABCD,这些都可以用组合数算,就是从除了选中行/列以外的格子里选k个,例如A=C[n*m-m][k],AB=C[n*m-n-m+1][k]。

因为题目中的模数不是质数,所以不能用常规打表阶乘逆元的方法算,因为数据范围很小,所以可以用递推转移的方法打表,首先初始化C[i][0]和C[i][i]都是1,对于1到i-1,有c[i][j]=C[i-1]j]+C[i-1][j-1],我们将15个几何都算出来求和即可。

#include<bits/stdc++.h>
//#include<__msvc_all_public_headers.hpp>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
const ll MOD = 1e6 + 7;
ll n;
ll inv[N], fac[N];
ll C[405][405];
void calC()
{//打表预处理组合数
    C[0][0] = 1;
    for (int i = 1; i <= 400; i++)
    {
        C[i][0] = C[i][i] = 1;//边界都是1
        for (int j = 1; j < i; j++)
        {//中间用类似杨辉三角的形式转移
            C[i][j] = C[i - 1][j] + C[i - 1][j - 1] % MOD;
        }
    }
}
void solve(int t)
{
    ll m, k;
    cin >> n >> m >> k;
    ll ans = C[n * m][k];//总方案数
    ans = (ans - C[n * m - n][k] + MOD) % MOD;//A
    ans = (ans - C[n * m - n][k] + MOD) % MOD;//B
    ans = (ans - C[n * m - m][k] + MOD) % MOD;//C
    ans = (ans - C[n * m - m][k] + MOD) % MOD;//D
    ans = (ans + C[n * m - n - m + 1][k]) % MOD;//AB
    ans = (ans + C[n * m - n - m + 1][k]) % MOD;//AD
    ans = (ans + C[n * m - n - n][k]) % MOD;//AC
    ans = (ans + C[n * m - m - m][k]) % MOD;//BD
    ans = (ans + C[n * m - m - n + 1][k]) % MOD;//BC
    ans = (ans + C[n * m - n - m + 1][k]) % MOD;//CD
    ans = (ans - C[n * m - 2 * n - m + 2][k] + MOD) % MOD;//ABC
    ans = (ans - C[n * m - 2 * n - m + 2][k] + MOD) % MOD;//ACD
    ans = (ans - C[n * m - 2 * m - n + 2][k] + MOD) % MOD;//BCD
    ans = (ans - C[n * m - 2 * m - n + 2][k] + MOD) % MOD;//ABD
    ans = (ans + C[n * m - 2 * m - 2 * n + 4][k]) % MOD;//ABCD
    cout << "Case " << t << ": " << ans % MOD << endl;
}
int main()
{

    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    calC();
    int cnt = 0;
    while (t--)
    {
        cnt++;
        solve(cnt);       
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timidcatt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值