main
黄海 2 years ago
parent 6fa11f8ae6
commit b17798a763

@ -1,59 +1,50 @@
#include <set>
#include <map>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <string>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <iomanip>
#include <iostream>
#include <algorithm>
#define clr(str, x) memset(str, x, sizeof(str))
#define FRER() freopen("in.txt", "r", stdin);
#define FREW() freopen("out.txt", "w", stdout);
#define MAX_INF 0x7fffffff
#define INF 0x3f3f3f3f
#define maxn
typedef long long int ll;
#include <bits/stdc++.h>
using namespace std;
const int mod = 1000007;
int C[405][405];
int n, m, k, ans = 0;
void init() {
for (int i = 1; i <= 400; i++) {
const int N = 410;
int C[N][N];
int n, m, k, ans;
/*
Sample Input
2
2 2 1
2 3 2
Sample Output
Case 1: 0
Case 2: 2
*/
int main() {
#ifndef ONLINE_JUDGE
freopen("UVA11806.in", "r", stdin);
#endif
// 预处理出组合数结果数组
for (int i = 1; i < N; i++) {
C[i][0] = C[i][i] = 1;
for (int j = 1; j < i; j++)
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
}
int main() {
// FRER()
// FREW()
init();
int T, kase = 1;
int s1, s2, s3, s4, s5;
scanf("%d", &T);
int T, cas = 1;
int S, s1, s2, s3, s4;
cin >> T;
while (T--) {
ans = 0;
scanf("%d%d%d", &n, &m, &k);
if (k == 0) {
printf("Case %d: 0\n", kase++);
ans = 0; // 多组测试数据,每次注意清零
cin >> n >> m >> k; // n行,m列,k个人
if (k == 0) { // 一定要注意边界情况比如0个人
printf("Case %d: 0\n", cas++);
continue;
}
s1 = C[n * m][k];
s2 = 2 * (C[n * (m - 1)][k] + C[(n - 1) * m][k]) % mod;
s3 = (C[(n - 2) * m][k] + 4 * C[n * m - n - m + 1][k] + C[n * (m - 2)][k]) % mod;
s4 = 2 * (C[(n - 2) * (m - 1)][k] + C[(n - 1) * (m - 2)][k]) % mod;
s5 = C[(n - 2) * (m - 2)][k] % mod;
S = C[n * m][k]; // n*m个格子中找出k个格子站人就是所有方案数
s1 = 2 * (C[n * (m - 1)][k] + C[(n - 1) * m][k]) % mod;
s2 = (C[(n - 2) * m][k] + 4 * C[n * m - n - m + 1][k] + C[n * (m - 2)][k]) % mod;
s3 = 2 * (C[(n - 2) * (m - 1)][k] + C[(n - 1) * (m - 2)][k]) % mod;
s4 = C[(n - 2) * (m - 2)][k] % mod;
ans = s1 - s2 + s3 - s4 + s5;
ans = (ans + 10 * mod) % mod; // 防止取余后出现负数
printf("Case %d: %d\n", kase++, ans);
ans = (S - s1 + s2 - s3 + s4) % mod; // 容斥原理
ans = (ans + mod) % mod; // 防止取余后出现负数
printf("Case %d: %d\n", cas++, ans); // 输出答案
}
return 0;
}

@ -787,15 +787,46 @@ signed main() {
}
```
### [$UVA$ $11806$ $Cheerleaders$](https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2906)
https://blog.csdn.net/Vici__/article/details/86742518
### [$UVA$ $11806$ $Cheerleaders$](https://vjudge.net/problem/UVA-11806)
#### 题意
总共有$k$个啦啦队队员,站在$n*m$的格子里,每条边至少有一个人,问有几种方法。一个角落可以同时属于两条边。
给定$n、m、k$三个数,$n$代表行数,$m$代表列数,$k$代表人数。
$n*m$的表格,一共有$k$个人,要求:
① 每个小格只能容纳一个人,所有人必须都在表格中。
**第一行、第一列、最后一行、最后一列必须站人**。
**若夹角处站人,则代表此行和列都已站人**。
问:有多少种方法?
#### 解题思路
利用容斥原理和状态压缩。$0001001001001000$分别表示上下左右没人,则$0000$表示四条边都有人;$0011$表示其中上下两边没人。以此类推,共有$16$种组合方式。
利用容斥原理,设:
- $S$为总数。($n*m$中选$k$个位置,即$C_{nm}k$
- $A$为第一行没有站人。(少一行)
- $B$为最后一行没有站人。(少一行)
- $C$为第一列没有站人。(少一列)
- $D$为最后一列没有站人。(少一列)
文氏图如下:
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202312140842345.png)
采用 **补集思想**:所有可能的站法 $-$ 所有不可能的站法 $=$ 符合条件的所有站法。
我们要计算的就是,$S$减去$ABCD$覆盖部分。
容斥原理公式表示为:
$ans=S(A+B+C+D)+(AB+AC+AD+BC+BD+CD)(ABC+ABD+ACD+BCD)+(ABCD)$
记:
如果上下两边没人那么,高度变为$n-2$,所以有$C((n-2)*m,k)$种方式。用这样的方式去计算。
$s_1=(A+B+C+D)$
$s_2=(AB+AC+AD+BC+BD+CD)$
$s_3=(ABC+ABD+ACD+BCD)$
$s_4=(ABCD)$
```cpp {.line-numbers}
#include<iostream>

Loading…
Cancel
Save