ARC100E Or Plus Max

ARC100E Or Plus Max

位运算上的比大小问题通常都很难处理,因此一般都是先把这种条件转化。

考虑下面三个集合:

  • \(A_K = \{(i, j) \mathop | i \operatorname{or} j \le K \and i \ne j\}\)
  • \(B_K = \{(i, j) \mathop | i, j \subseteq K\and 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\and 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 \and 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值