Cheerleaders UVA - 11806 (容斥原理)

题述:对于一个n*m的广场,共有n*m个格子,要放k个东西,每个格子上只能放一个东西且第一行、最后一行、第一列和最后一列上都必须至少放一个东西。在四个顶点的格子相当于同时占了一行和一列。

思路:训练赛的时候第一反应是也许可以枚举所有的情况,但稍微一想发现那是不现实的,就转去做其他题目了,结果一个dfs因为姿势不对tle到无奈,所以最后没时间写这道题。赛后仔细想了想,又结合网上的文字叙述自己推了一遍。这道题情况太多故只能考虑他的相反情况,最后用所有放法减去他的所有相反情况。相反情况就是第一行或最后一行或第一列或最后一列上不能放东西,说到或应该很容易想到得用容斥原理,分别用0 1 2 3代表第一行不放、第一列不放、最后一行不放、最后一列不放,四个元素可以用0~16枚举其所有情况,0000代表四个都放,1111代表四个都不放。容斥原理是加上奇数个减去偶数个,这里是要减去这些相反情况,故是减去奇数个,加上偶数个。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstdlib>
#include<sstream>
#include<deque>
#include<stack>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-6;
const int maxn = 400 + 5;
const int mod = 1000007;
const int dx[] = {1, -1, 0, 0, -1, -1, 1, 1};
const int dy[] = {0, 0, -1, 1, -1, 1, -1, 1};
const int Dis[] = {-1, 1, -5, 5};
const double inf = 0x3f3f3f3f;
int n, m, k;
ll c[maxn][maxn];
void init(){
    c[0][0] = 1;
    for(int i = 1; i < maxn; ++i){
        c[i][0] = c[i][i] = 1;
        for(int j = 1; j < i; ++j){
            c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
        }
    }
}
int main(){
    init();
    int t, kase = 0;
    scanf("%d", &t);
    while(t--){
        scanf("%d%d%d", &n, &m, &k);
        vector<int> vec;
        ll ans = c[n * m][k];
        for(int i = 0; i < 16; ++i){
            vec.clear();
            for(int j = 0; j < 4; ++j){
                if(i & (1 << j)) vec.push_back(j);//将0 1 2 3中不能放东西的加到集合中。
            }

            int len = vec.size();
            ll tmp = 0;
            if(len == 1){
                if(vec[0] & 1) tmp = c[n * m - n][k];
                else tmp = c[n * m - m][k];
            }
            else if(len == 2){
                if((vec[0] & 1) && (vec[1] & 1)) tmp = c[n * (m - 2)][k];
                else if(!(vec[0] & 1) && !(vec[1] & 1)) tmp = c[(n - 2) * m][k];
                else tmp = c[(n - 1) * (m - 1)][k];
            }
            else if(len == 3){
                if((vec[0] + vec[1] + vec[2]) & 1) tmp = c[(n - 2) * (m - 1)][k];
                else tmp = c[(n - 1) * (m - 2)][k];
            }
            else if(len == 4){
                tmp = c[(n - 2) * (m - 2)][k];
            }
            if(len & 1) ans = (ans - tmp + mod) % mod;
            else ans = (ans + tmp) % mod;
        }
        printf("Case %d: %lld\n", ++kase, ans);
    }
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值