CF1641D
题面
给你 \(n\) 个集合 \(S_i\),每个集合有 \(m\) 个元素,集合 \(i\) 的权值为 \(w_i\) ,你需要求 \(\min\{S_i\cap S_j=\varnothing |w_i+w_j\}\) 。
数据范围:\(n\le 10^5,m\le 5\)。
题解
首先,这个交为空就很难搞,对于这种有多个元素的集合做匹配我们都只能hash来判是否相等。
但是bitset
就没有这种限制,如果我们给每个元素开一个 bitset
,这样就可以对每一个元素 \(O(\frac{nm}{32})\) 的求出交不为空的,然后取反就是交为空的,为了求最小值我们可以最开始按 \(w_i\) 排序,这样就可以用 _Find_first()
得到最小值。
但是这样就会有 \(O(\frac{nm}{32})\) 的空间复杂度,直接GG了,当然,如果你根号分治一波貌似空间也是可以卡过去的,但是实际上能不能过呢?小编也想知道。
这里主要介绍的是另外一种做法——容斥!
怎么个容斥法呢?
对于 \(S_i\) 来说,若 \(S_j\cap S_j\neq\varnothing\) ,那么 \(\sum\limits_{T\subseteq S_j}(-1)^{|T|-1} [T\subseteq S_i\and T\neq \varnothing]=1\) 。
这个的证明应该是二项式定理,就是考虑每个元素的贡献次数,和容斥的证明基本一样。
总之有了这个式子之后,因为加法的可加性,我们可以快速求出当前有几个集合和 \(S_i\) 有交,所以按 \(w_i\) 排序之后就可以二分求了。
但是为什么我们要给每一个 \(i\) 都求出对于的最小值呢?直接双指针就可以消除掉二分的 \(\log\) 。
扩展
如果题目给的是数列(也就是元素间有序)怎么办?
感觉可以按照[JOISC 2018 Day 4] 野猪 那样,但是初步想了一下发现也要 \(2^m\) 个状态,不知道能不能继续优化。
启发
- 判断两个集合是否有交的方法。
- 只要求全局最小值的时候是可以直接双指针的。