例题2.3 拉拉队 UVa11806

1.题目描述:点击打开链接

2.解题思路:本题利用容斥原理解决。首先我们解决一个简单的问题:如何求解“第一行,最后一行,第一列,最后一列都没有石子”的方案数。这相当于只有m-2行和n-2列,答案为C((m-2)(n-2),k)。那么如果我们可以想办法把本题分解为一系列这个简单问题的线性组合,即可求解出答案。这正是容斥原理的作用。

设满足“第一行没有石子”的方案集为A,最后一行没有石子的方案集为B,第一列没有石子的方案集为C,最后一列没有石子的方案集为D,全集为S。那么问题的答案就是“在S中但不在A,B,C,D任何一个子集中”的元素个数。这正好就是容斥原理的经典问题。

在程序中,我们用二进制表示A,B,C,D的所有“搭配”。如果在集合A或集合B中,就相当于少了一行;如果在集合C或集合D中,就相当于少了一列。假设最后只剩下了r行c列,那么方案数就是C(rc,k)。而这样的搭配一共只有16种情况。

注意:在应用容斥原理时,不要试图一步到位写出完整的公式,而应该逐个逐个地依次计算,只要有奇数个条件,就做减法, 偶数个条件就做加法。这就是容斥原理的基本运算方法。

3.代码:

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
typedef pair<long long, long long> PL;
#define me(s) memset(s,0,sizeof(s))
#define For(i,n) for(int i=0;i<(n);i++)

const int MOD = 1000007;
const int K = 500 + 10;
int C[K][K];

void init()
{
	me(C);
	C[0][0] = 1;
	for (int i = 0; i < K; 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()
{
	//freopen("t.txt", "r", stdin);
	init();
	int T;
	scanf("%d", &T);
	int rnd = 0;
	while (T--)
	{
		int n, m, k, sum = 0;
		scanf("%d%d%d", &n, &m, &k);
		for (int S = 0; S < 16; S++)//一共16种搭配
		{
			int b = 0, r = n, c = m;//b统计搭配的个数,r,c是可放置的行数和列数
			if (S & 1){ r--; b++; }
			if (S & 2){ r--; b++; }
			if (S & 4){ c--; b++; }
			if (S & 8){ c--; b++; }
			if (b & 1)sum = (sum + MOD - C[r*c][k]) % MOD;//奇数个搭配,做减法
			else sum = (sum + C[r*c][k]) % MOD;//偶数个搭配,做加法
		}
		printf("Case %d: %d\n", ++rnd, sum);
	}
	return 0;
}

你可能感兴趣的:(容斥原理)