UVa 11806 Cheerleaders (组合逆向思维||容斥定理)

2024-03-05 20:32

本文主要是介绍UVa 11806 Cheerleaders (组合逆向思维||容斥定理),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

11806 - Cheerleaders

Time limit: 2.000 seconds

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2906


【题意】

在一个n*m的区域内放k个棋子,第一排,最后一排,第一列,最后一列一定要放,求一共有多少种方法。


【思路】

正着想重复的情况太多,不妨反着思考。

设ai表示有且仅有i条边上没有放的情况数,我们想要的显然是a1+a2+a3+a4,这就是所有不符合要求的情况,但是同样不好直接计算。

但是有一个方便计算的:设si表示i条边上不能放,而其他的地方随便放的情况数,

则有


同时


解得

a1+a2+a3+a4 = s1-s2+s3-s4


【完整代码】

/*0.012s*/#include<cstdio>
const int mod = 1000007;int c[405][405];int main(void)
{///生成组合数c[0][0] = 1;for (int i = 1; i < 405; 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;}///endint t, cas = 0, n, m, k, ans, s1, s2, s3, s4;scanf("%d", &t);while (t--){scanf("%d%d%d", &n, &m, &k);if (n * m < k) ans = 0;else{s1 = (c[n * m - m][k] + c[n * m - n][k]) << 1;s2 = c[n * m - m - m][k] + c[n * m - n - n][k] + (c[n * m - n - m + 1][k] << 2);s3 = (c[n * m - n - m - m + 2][k]  + c[n * m - m - n - n + 2][k]) << 1;s4 = c[n * m - m - m - n - n + 4][k];ans = ((c[n * m][k] - s1 + s2 - s3 + s4) % mod + mod) % mod;}printf("Case %d: %d\n", ++cas, ans);}return 0;
}

【容斥定理的解法】

/*0.016s*/#include<cstdio>
const int MOD = 1000007;
const int MAXK = 500;int C[MAXK + 10][MAXK + 10];int main()
{C[0][0] = 1;for (int i = 0; i <= MAXK; ++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 T;scanf("%d", &T);for (int kase = 1; kase <= T; ++kase){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++;} // 第一行没有石头,可以放石头的行数r减1if(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", kase, sum);}return 0;
}

这篇关于UVa 11806 Cheerleaders (组合逆向思维||容斥定理)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/777726

相关文章

hdu4869(逆元+求组合数)

//输入n,m,n表示翻牌的次数,m表示牌的数目,求经过n次操作后共有几种状态#include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdlib.h>#includ

hdu4407(容斥原理)

题意:给一串数字1,2,......n,两个操作:1、修改第k个数字,2、查询区间[l,r]中与n互质的数之和。 解题思路:咱一看,像线段树,但是如果用线段树做,那么每个区间一定要记录所有的素因子,这样会超内存。然后我就做不来了。后来看了题解,原来是用容斥原理来做的。还记得这道题目吗?求区间[1,r]中与p互质的数的个数,如果不会的话就先去做那题吧。现在这题是求区间[l,r]中与n互质的数的和

uva 10055 uva 10071 uva 10300(水题两三道)

情歌两三首,水题两三道。 好久没敲代码了为暑假大作战热热身。 uva 10055 Hashmat the Brave Warrior 求俩数相减。 两个debug的地方,一个是longlong,一个是输入顺序。 代码: #include<stdio.h>int main(){long long a, b;//debugwhile(scanf("%lld%lld", &

poj 3259 uva 558 Wormholes(bellman最短路负权回路判断)

poj 3259: 题意:John的农场里n块地,m条路连接两块地,w个虫洞,虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退Ts。 任务是求你会不会在从某块地出发后又回来,看到了离开之前的自己。 判断树中是否存在负权回路就ok了。 bellman代码: #include<stdio.h>const int MaxN = 501;//农场数const int

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

uva 10387 Billiard(简单几何)

题意是一个球从矩形的中点出发,告诉你小球与矩形两条边的碰撞次数与小球回到原点的时间,求小球出发时的角度和小球的速度。 简单的几何问题,小球每与竖边碰撞一次,向右扩展一个相同的矩形;每与横边碰撞一次,向上扩展一个相同的矩形。 可以发现,扩展矩形的路径和在当前矩形中的每一段路径相同,当小球回到出发点时,一条直线的路径刚好经过最后一个扩展矩形的中心点。 最后扩展的路径和横边竖边恰好组成一个直

uva 10061 How many zero's and how many digits ?(不同进制阶乘末尾几个0)+poj 1401

题意是求在base进制下的 n!的结果有几位数,末尾有几个0。 想起刚开始的时候做的一道10进制下的n阶乘末尾有几个零,以及之前有做过的一道n阶乘的位数。 当时都是在10进制下的。 10进制下的做法是: 1. n阶位数:直接 lg(n!)就是得数的位数。 2. n阶末尾0的个数:由于2 * 5 将会在得数中以0的形式存在,所以计算2或者计算5,由于因子中出现5必然出现2,所以直接一

uva 568 Just the Facts(n!打表递推)

题意是求n!的末尾第一个不为0的数字。 不用大数,特别的处理。 代码: #include <stdio.h>const int maxn = 10000 + 1;int f[maxn];int main(){#ifdef LOCALfreopen("in.txt", "r", stdin);#endif // LOCALf[0] = 1;for (int i = 1; i <=

uva 575 Skew Binary(位运算)

求第一个以(2^(k+1)-1)为进制的数。 数据不大,可以直接搞。 代码: #include <stdio.h>#include <string.h>const int maxn = 100 + 5;int main(){char num[maxn];while (scanf("%s", num) == 1){if (num[0] == '0')break;int len =

uva 10014 Simple calculations(数学推导)

直接按照题意来推导最后的结果就行了。 开始的时候只做到了第一个推导,第二次没有继续下去。 代码: #include<stdio.h>int main(){int T, n, i;double a, aa, sum, temp, ans;scanf("%d", &T);while(T--){scanf("%d", &n);scanf("%lf", &first);scanf