AT4168 [ARC100C] Or Plus Max

Or Plus Max

给你一个长度为 \(2^n\) 的序列 \(a\),每个 \(1\le K\le 2^n-1\),找出最大的 \(a_i+a_j \left( i\ | \ j\le K \ ,i,j \le 2^n\right)\) 并输出。

第一次接触这个比较有趣的思想,主要算法数高维前缀和

高维前缀和主要用于解决形似 \(\sum_{i\subset S} f(i)\) 的问题,是一种不可多得的人类智慧巧妙算法。

首先一维前缀和不用讲,但二维前缀和其实还有一种不为人知的形式:

for(int i = 1; i <= n; i ++)
    for(int j = 1; j <= n; j ++) a[i][j] += a[i - 1][j];
for(int i = 1; i <= n; i ++)
    for(int j = 1; j <= n; j ++) a[i][j] += a[i][j - 1];

简单分析一下还是比较显然的。然后拓展到三维:

for(int i = 1; i <= n; i ++)
    for(int j = 1; j <= n; j ++)
        for(int k = 1; k <= n; k ++) a[i][j][k] += a[i - 1][j][k];
for(int i = 1; i <= n; i ++)
    for(int j = 1; j <= n; j ++)
        for(int k = 1; k <= n; k ++) a[i][j][k] += a[i][j - 1][k];
for(int i = 1; i <= n; i ++)
    for(int j = 1; j <= n; j ++)
        for(int k = 1; k <= n; k ++) a[i][j][k] += a[i][j][k - 1];

看出规律了对吧,逐次按位考虑,每次微调一维。于是利用位运算模拟维数,我们不难得到 \(n\) 维前缀和。

for(int j = 0; j < n; j ++)
    for(int i = 0; i < (1 << n); i ++)
        if(i >> j & 1) a[i] += a[i ^ (1 << j)];

然而基础算法是一方面,应用又是一方面。

本题我们按照 \(K\) 的不同来考虑,设 \(A_k=\max_{i|j=k}\{a_i+a_j\}\),那么 \(ans_k=\max_{1\leq i\leq k}\{A_i\}\)

考虑到 \(\max\) 的区间可交性,不妨将限制放宽,与 ST 表的原理类似,设 \(A_k=\max_{i,j\subset k}\{a_i+a_j\}\)

然后就是比较裸的高维前缀和问题,只是把求和变为取 \(\max\) 了,然后每个数代表两个状态,即最大和次大值。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 1 << 19;
const int INF = 1 << 30;
int n;
struct node{int x, y;} a[N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

node Merge(node a, node b){
	if(a.x < b.x) swap(a, b);
	node now = a;
	if(a.y < b.x) now.y = b.x;
	return now;
}

int main(){
	n = read();
	for(int i = 0; i < (1 << n); i ++){
		a[i].x = read();
		a[i].y = - INF;
	}
	for(int j = 0; j < n; j ++)
		for(int i = 0; i < (1 << n); i ++)
			if(i >> j & 1) a[i] = Merge(a[i], a[i ^ (1 << j)]);
	int ans = 0;
	for(int i = 1; i < (1 << n); i ++){
		ans = max(ans, a[i].x + a[i].y);
		printf("%d\n", ans);
	}
	return 0;
}
posted @ 2021-07-08 22:02  LPF'sBlog  阅读(52)  评论(0编辑  收藏  举报