ARC100E Or Plus Max
位运算上的比大小问题通常都很难处理,因此一般都是先把这种条件转化。
考虑下面三个集合:
- \(A_K = \{(i, j) \mathop | i \operatorname{or} j \le K \land i \ne j\}\)。
- \(B_K = \{(i, j) \mathop | i, j \subseteq K\land i \ne j\}\)。
- 这里 \(i \subseteq K\) 的意思,可以用以下四种等价角度理解:
- \(i \operatorname{bitand} K = i\)。\(\operatorname{bitand}\) 是按位与的意思。
- 同一个二进制位上,\(i\) 的这一位小于等于 \(K\) 的这一位。
- 同一个二进制位上,\(K\) 这一位为 \(1\),则 \(i\) 这一位可以为 \(1\) 或 \(0\);\(K\) 这一位为 \(0\),则 \(i\) 这一位只能为 \(0\)。
- \(i\) 的所有为 \(1\) 的二进制位数集,是 \(K\) 的所有为 \(1\) 的二进制位数集的子集。
- \(C_K = \{(i, j)\mathop | i \operatorname{or} j = K\land i \ne j\}\)。
不难发现 \(C_K \subseteq B_K \subseteq A_K\),以及 \(A_K = \bigcup\limits_{i=1}^K C_i\)。
不难发现 \(\bigcup\limits_{i=1}^K C_i \subseteq \bigcup\limits_{i=1}^K B_i\),而 \(\bigcup\limits_{i=1}^K B_i \subseteq A_K\)。所以,\(A_K = \bigcup \limits_{i = 1}^KB_i\)。唯一不同点在于,原先 \(C_i\) 之间互不相交,而 \(B_i\) 有交。
用语言说明这个转化的正确性:\(B_i\) 包含满足 \(i \operatorname{or} j = K\) 的全部 \((i, j)\),也不把 \(i \operatorname{or} j >K\) 的 \((i, j)\) 给放进来,所以正确。
为什么要这么转化?两个原因。
【原因一】
原先 \(i \operatorname{or} j \le K\) 或 \(i \operatorname{or} j = K\) 都是一条二元制约关系。
而 \(i, j \subseteq K\) 可以理解为 \(i \subseteq K \land j \subseteq K\),即两条一元制约关系,会更好处理。
当然一直还有一个二元制约关系是 \(i \ne j\),不过这个通常也不难处理。
【原因二】
\(i \subseteq K\) 是一个套路性的 sosdp 形式。
不难发现 \(\max\) 是可重复贡献的(即 \(\max(x, x) = x\)),所以上面的并集运算可以直接转 \(\max\)。
假设数对 \((i, j)\) 的权值是 \(a_i + a_j\),那么要求的答案可以看做 \(\max(A_K) = \max_{i = 1}^K \max(B_i)\)
因此考虑求 \(\max(B_k) = \max\{a_i + a_j \mathop | i, j \subseteq k\}\)。
不难发现,我们只需要维护满足 \(i \subseteq k\) 的 \(a_i\) 的最大值和次大值分别作为 \(a_i\) 和 \(a_j\),就能让和取到最大值。
而最大值和次大值与和一样,可以方便地合并。这题做完了。
/*
* @Author: crab-in-the-northeast
* @Date: 2023-04-17 10:20:23
* @Last Modified by: crab-in-the-northeast
* @Last Modified time: 2023-04-17 11:06:28
*/
#include <bits/stdc++.h>
inline int read() {
int x = 0;
bool f = true;
char ch = getchar();
for (; !isdigit(ch); ch = getchar())
if (ch == '-')
f = false;
for (; isdigit(ch); ch = getchar())
x = (x << 1) + (x << 3) + ch - '0';
return f ? x : (~(x - 1));
}
inline bool gmx(int &a, int b) {
return b > a ? a = b, true : false;
}
const int maxn = 19;
std :: vector <int> f[1 << maxn];
int main() {
int n = read();
for (int i = 0; i < (1 << n); ++i)
f[i].push_back(read());
for (int j = 0; j < n; ++j) {
for (int i = 0; i < (1 << n); ++i) {
if (i & (1 << j)) {
int lst = i ^ (1 << j);
for (int x : f[lst])
f[i].push_back(x);
std :: sort(f[i].begin(), f[i].end(), std :: greater <int> ());
while (f[i].size() > 2)
f[i].pop_back();
}
}
}
int ans = 0;
for (int K = 1; K < (1 << n); ++K) {
gmx(ans, f[K][0] + f[K][1]);
printf("%d\n", ans);
}
return 0;
}