传送门:Or Plus Max 高维前缀和
题目描述
長さ 2N の整数列 A0, A1, ..., A2N−1 があります。(添字が 0 から始まることに注意)
1 ≤ K ≤ 2N−1 を満たすすべての整数 K について、次の問題を解いてください。
- i,j を整数とする。0 ≤ i < j ≤ 2N−1, (i or j) ≤ K のとき、Ai + Aj の最大値を求めよ。 ただしここで or はビットごとの論理和を表す。
输入格式
入力は以下の形式で標準入力から与えられる。
N A0 A1 ... A2N−1
输出格式
2N−1 行出力せよ。 i 行目には、K=i のときの上記の問題の答えを出力せよ。
题意翻译
给你一个长度为 2n 的序列 a,每个1≤K≤2n−1,找出最大的 ai+aj(iorj≤K,0≤i<j<2n)并输出。
or 表示按位或运算。
输入输出样例
输入 #1
2 1 2 3 1
输出 #1
3 4 5
输入 #2
3 10 71 84 33 6 47 23 25
输出 #2
81 94 155 155 155 155 155
输入 #3
4 75 26 45 72 81 47 97 97 2 2 25 82 84 17 56 32
输出 #3
101 120 147 156 156 178 194 194 194 194 194 194 194 194 194
题解
思路:
下面是详细注释后的代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long // 定义long long类型别名
const int INF = 1e9 + 1; // 定义一个极大值INF,用于初始化次大值
// 定义一个结构体Data,用于存储最大值和次大值
struct Data {
ll first, second; // first存储最大值,second存储次大值
// 重载+运算符,用于合并两个Data对象
Data operator+(const Data &t) {
Data y;
if (first > t.first) { // 如果当前最大值大于t的最大值
y.first = first; // 更新最大值
y.second = max(t.first, second); // 更新次大值为t的最大值和当前次大值的较大者
} else { // 否则
y.first = t.first; // 更新最大值为t的最大值
y.second = max(t.second, first); // 更新次大值为t的次大值和当前最大值的较大者
}
return y;
}
} A[1 << 19]; // 定义一个大小为2^19的Data数组A,用于存储每个子集的最大值和次大值
int main() {
int n;
cin >> n; // 输入n,表示集合的大小
ll m = 1 << n; // 计算m = 2^n,表示所有子集的数量
// 初始化数组A
for (ll i = 0; i < m; i++) {
cin >> A[i].first; // 输入每个子集的初始值
A[i].second = -INF; // 将次大值初始化为-INF
}
// 动态规划计算每个子集的最大值和次大值
for (int i = 0; i < n; i++) { // 遍历每一位
for (int j = 0; j < m; j++) { // 遍历所有子集
if ((j >> i) & 1) { // 如果子集j的第i位为1
A[j] = A[j] + A[j ^ (1 << i)]; // 合并子集j和子集j^(1<<i)的最大值和次大值
}
}
}
ll ans = 0; // 初始化答案为0
for (int i = 1; i <= m - 1; i++) { // 遍历所有非空子集
ans = max(ans, A[i].first + A[i].second); // 更新答案为当前子集的最大值和次大值之和的最大值
cout << ans << endl; // 输出当前答案
}
return 0;
}