容斥

容斥及子集枚举

容斥

容斥又称小学奥数,属于数论的一部分,在了解容斥之前应该先学会集合和venn图。

定义

在计数时,必须注意没有重复,没有遗漏。为了使重叠部分不被重复计算,人们研究出一种新的计数方法,这种方法的基本思想是:先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理。(摘自百度百科)。

例子

img
在这个图片中我们可以看到图片中看到加入我们要求图中所占的面积的话,则需要 将A+B+C-D-E-F+G求出来,然后我们再轻松地推一些别的规律发现一条很重要的规律——奇加偶减(有时候是偶加奇减,反正是一加一减)。所以记住这一点容斥基本就学会了,主要就在实现上了。

题目

让你求出1到1000内被2,4,5,整除的数的个数。

且题目限制如果暴力的话是过不了的,此时就需要我们用到容斥了,首先我们要知道,在做这种题的同时,一定要先将所有数都转换成互质的数,因为如果不互质的话,结果并不影响,只是会多计算几次。所以原题就成了求2,5的个数。

然后我们就可以用奇加偶减的特性,1000被2整除的有500个,被5整除的有200个加起来有700个,再减去被10整除的个数100个,所以总共有600个。

子集枚举

容斥需要把所有情况都枚举一遍,此时就需要用到子集枚举了,也是一种可以骗分的一种好方法。

就是当n十分小的时候,我们可以将0到(2^n)内所有的数都枚举一遍,然后转变成二进制的形式,然后就可以通过判断每一位是否为一,如果是的话,就说明位置上的数是可以被选到集合里去的。

代码:

	for (int i = 0; i <= (1 << n); i++, puts(""))
		for (int j = 1; j <= n; j++)
			if (i & (1 << j - 1))
				printf("%d ", a[j]);
原文地址:https://www.cnblogs.com/liuwenyao/p/9844013.html