diff --git a/TangDou/AcWing_TiGao/T5/RongChi/UVA11806.cpp b/TangDou/AcWing_TiGao/T5/RongChi/UVA11806.cpp index 150d9e9..e777e6a 100644 --- a/TangDou/AcWing_TiGao/T5/RongChi/UVA11806.cpp +++ b/TangDou/AcWing_TiGao/T5/RongChi/UVA11806.cpp @@ -1,59 +1,50 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 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; } \ No newline at end of file diff --git a/TangDou/AcWing_TiGao/T5/RongChi/UVA11806.in b/TangDou/AcWing_TiGao/T5/RongChi/UVA11806.in new file mode 100644 index 0000000..3698979 --- /dev/null +++ b/TangDou/AcWing_TiGao/T5/RongChi/UVA11806.in @@ -0,0 +1,3 @@ +2 +2 2 1 +2 3 2 \ No newline at end of file diff --git a/TangDou/Topic/容斥原理.md b/TangDou/Topic/容斥原理.md index 35b585f..1831dd2 100644 --- a/TangDou/Topic/容斥原理.md +++ b/TangDou/Topic/容斥原理.md @@ -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$个人,要求: + +① 每个小格只能容纳一个人,所有人必须都在表格中。 +② **第一行、第一列、最后一行、最后一列必须站人**。 +③ **若夹角处站人,则代表此行和列都已站人**。 + +问:有多少种方法? #### 解题思路 -利用容斥原理和状态压缩。$0001,0010,0100,1000$分别表示上下左右没人,则$0000$表示四条边都有人;$0011$表示其中上下两边没人。以此类推,共有$16$种组合方式。 +利用容斥原理,设: + +- $S$为总数。($n*m$中选$k$个位置,即$C_{n∗m}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