C - Coverage Editorial by en_translator
Given a set of sets, how can we determine if they satisfy the conditions in the Problem Statement?
The decision problem can be solved as follows.
- We have a set \(T\) of integers, initialized as an empty set.
- Let \(s_1, s_2, \dots, s_k\) be the chosen set. Perform the following operation on \(i=1, 2, \dots, k\).
- Add to \(T\) all the integers contained in \(s_i\). (If an integer is already added to \(T\), it is not added twice.)
- After the operations above are completed, the answer is Yes if \(T\) contains all of \(1, 2, \dots, N\), and No otherwise.
This algorithm works in a total of \(\mathrm{O}(N M \log N)\) by managing \(T\) with a set type like a std::set
in C++ or the “dict” type in Python. Thus, if we can solve the decision problem for all the \(2^M-1\) ways of choice, the problem can be solved in a total of \(\mathrm{O}(2^M N M \log N)\) time.
We can enumerate all the ways to choose the sets with a technique called bit bruteforcing.
(Here, we assume that the sets are indexed 0-based: \(S_0, S_1, \dots, S_{M-1}\).) The bit bruteforcing is a method to enumerate the choices of sets by associating each them with an integer \(x\) between \(0\) and \(2^M-1\) (inclusive), as follows:
- (The \(0\)-th bit of \(x\) in binary is \(1\)) \(\iff\) (Set \(S_0\) is chosen)
- (The \(1\)-th bit of \(x\) in binary is \(1\)) \(\iff\) (Set \(S_1\) is chosen)
- \(\vdots\)
- (The \((M-1)\)-th bit of \(x\) in binary is \(1\)) \(\iff\) (Set \(S_{M-1}\) is chosen)
(Here, “the \(i\)-th bit” refers to the \(2^i\)s place in binary.)
For example, if \(M = 3\) and \(x = 3\), then \(3\) in base three is 011
, so we choose \(S_0\) and \(S_1\) here.
Such a correspondence allows us to map every way to choose sets to every integer between \(0\) and \(2^M-1\). For example, if \(M=3\), then the integers between \(0\) and \(2^3-1 = 7\) are, in base 2, as follows:
000
001
010
011
100
101
110
111
As you can see in the example above, every way to choose sets has a corresponding integer within the range. For example, choosing \(S_0\) and \(S_2\) corresponds to a base-2 integer 101
, which corresponds to \(5\).)
With such a correspondence, we can scan every integer between \(0\) and \(2^M-1\) without a for loop and convert the integer into a set, in order to inspect all the ways to choose without duplicates. Also, extracting the \(i\)-th bit of the integer \(x\) can be found as (x >> i) & 1
thanks to the right-shift and logical product operators, which enables us to convert from an integer to a set correctly.
With the bit-bruteforcing described above, the problem can be solved in a total of \(\mathrm{O}(2^M N M \log N)\) time.
Sample code (C++):
#include <iostream>
#include <set>
#include <vector>
using namespace std;
int main() {
int N, M;
cin >> N >> M;
vector<vector<int>> a(M);
for (int i = 0; i < M; i++) {
int c;
cin >> c;
a[i].resize(c);
for (auto& x : a[i]) cin >> x;
}
int ans = 0;
for (int b = 0; b < (1 << M); b++) {
set<int> s;
for (int i = 0; i < M; i++) {
if ((b >> i) & 1) {
for (auto& x : a[i]) s.insert(x);
}
}
ans += (int)s.size() == N;
}
cout << ans << "\n";
}
posted:
last update: