有N名士兵(编号从1到N)。每个士兵都拥有M种不同技能的一些技能(编号从1到M)。军队的技能组合是其组成士兵的技能组合。有多少不同的士兵满足哪些具有特定的技能要求
根据解释问题减少到找到这些数字的子集数量,其OR正好等于所需的值,比如req
设f(i)为数j的数量,使得j OR i = i。
则答案为
Σi(-1)^ popcount(i xor req)(2 ^ f(i)-1)对于所有我来说i或req是req
(−1)^popcount(i xor req)
请解释算法。
答案 0 :(得分:2)
让我们来看看问题的简化版本。假设您有2套A
,B
。假设您正在寻找具有正好1个元素的子集的大小。
首先,您计算|A| + |B|
。但是在这里,您在| A [intersection] B|
和|A|
中计算了两次交叉点|B|
的大小,因此您将其缩小并获得|A| + |B| - |A [intersection] B|
。
同样,对于3组,您首先要添加所有集的大小:|A| + |B| + |C|
。
然后,你减少所有"连接"在两组|A [intersection] B|, |A [intersection] C|, |B [intersection] C|
之间。但现在,您删除了太多元素,还删除了|A [intersection] B [intersection] C|
中的元素。要克服它,请添加此大小。
因此,概括它 - 您获得了n
套和m
所需的属性:
#of items with m properties:
E(m) = sum { (-1)^(r-m) * Choose(r,m) * W(r) }
Where:
W(r) = summation of all intersections of size i, or formally:
W(r) = sum { W(p_k_1,...,p_k_r) | 1<=k_1<k_2<...<k_r<=n }
W(pk1,...,pkr) = |X_k1 [intersection] X_k2 [intersection] .... [intersection] X_kr|
这解释了(-1)^x
如何发挥作用,我们重复包含(添加)和排除(减少)尺寸。 (-1)^ x允许我们自动&#34;减少/添加求和中的每个元素。
正如你所说,popcount(i xor req)
正在计算&#34; up&#34; i^req
表示中的位。这意味着,popcount()
实际上是上述公式中的索引i
,您的集合为:X_i = all soldiers with required property i
。
这意味着如果k = popcount(i XOR req)
,i
完全得到k
&#34;必需&#34;属性,并且属于r=k
时的值的上述总和,因此属性add / substract取决于(-1)^k = (-1)^popcount(i XOR req)
的值
答案 1 :(得分:1)
在阅读社论并自己获得AC后,我不认为这里容易理解“包含 - 排除原则”的应用,这个问题围绕Codeforces Div.1问题C / D级别,例如,这一个:http://codeforces.com/contest/449/problem/D
重写版本@ 2016
以前的答案太乱了,我试着清理它并重写一个更易读的答案
我将解释为什么解决方案有效,但不 如何提出这样的解决方案,我认为这更多是关于体验。
大图解释
首先,不要过度思考问题,给定a[1..N]
,解决方案实际上是所有可以产生req
话虽如此,我们怎样才能找到可以产生x
的所有子集?
如解决方案所示,我们定义f(i)
让
f(i)
为j
或i
=i
的数字。
如果您觉得难以理解,请考虑其物理意义:
对于任何
i
,j
或i
=i
iffj
可由带走一些二进制位1远离i
例如,如果i
= 7,那么j
可以是0,1,2 ...,7;如果i
= 5,那么j
可以是0,1,4,5
因此,2^f(i) - 1
确实是当OR i
i
的可能子集的数量
在这里等一下,确保先完成上述部分。
现在如果i
= req
本身怎么办?这是什么意思? 2^f(req)-1
中计算了哪些子集? 2^f(req)-1
如何与我们想要的答案相关?
我们想要OR子集的所有元素将产生
req
的子集数量
2^f(req)-1
给出了所有元素ORreq
将产生req
你能闻到2^f(req)-1
可能比我们需要的更多吗?
这样考虑:有一些子集2^f(req)-1
计数,或者所有元素只能生成x
,其中x
可以通过从{{1中取走一些二进制位1来生成req
(见上面的方块引用)
所以,我们必须从2^f(req)-1
中删除一些内容,然后是包含 - 排除原则。
假设我们想要减去OR所有元素等于x
的子集,即req
带走一个二进制位1
有了类似的想法,你会发现必须减去2^f(x)-1
,对于所有可能的x
,在这里,请记住 x
是所有数字{{1}拿掉一个二进制位1
但是,你会减去超过你应该的数量,因为不同的req
可能会共享同一个x
,其中y
是y
带走两个二进制位1,或req
y
带走一个二进制位1,您可以从x
中删除每个y
一次,但是在减去2^f(req)-1
的过程中,对于某些2^f(x)-1
,您有多次减去!
通过包含 - 排除原则,您必须将它们添加回来......我们在这里有一点摘要:
我们想要的是
y
-2^f(req)-1
+2^f(x)-1
...其中2^f(y)-1
是一组数字等于x
删除一个二进制位1,req
是一组数字,等于y
删除两个二进制位1(或req
删除一个二进制位1)
你能看到这里的模式吗?是的,这是公式
中的x
部分
对于所有-1^popcount()
&lt; = i
, req
的每一位都与i
或0 相同,那么{{1等于req
(删除i xor req
的所有1位),所以req - i
确实从i
中删除了二进制位1的#为了得到popcount(i xor req)
综合整个故事,形成了公式:req
适用于所有i
,Sum (-1)^popcount(i xor req) * (2^f(i) - 1)
DP计算f(i)
i
以下是计算i OR req = req
请注意,它基于以下重现关系:
f(i)=
for(int i=0;i<20;i++) for(int j=0; j<=(1<<20); j++) { if(j&(1<<i)) { f[j] += f[j^(1<<i)]; } }
本身+ f(有些j是我删除了第1位)因此,如果我的第j位为1,则f(i)= 1 + f(i xor(1 <&lt; j))
请注意,f(i)
必须小于i
,因此DP订单。