AtCoder Regular Contest 100 - E Or Plus Max

题目

题意:

给你0-(2^n-1)个数,让你对每一个1<=k>=2^n-1的k,得出a[i]+a[j]最大,要求是i|j<=k。

POINT:

既然要求是i|j<=k,那么前面的答案就一定是后面的答案。

所以对于每一个K,我们要找i|j=k的答案就可以了。

找到k中的有1的数,即(1<<j&k成立),那么(1<<j)^k这个答案的就一定可以用在k这个答案里边。

而(1<<j)^k这个答案在前面已经算出。算完之后,k这里藏的两个最大数,不一定是或起来正好等于k。因为每个k藏的都是对于它的真子集。

也就是说, 100111 这个数,里面包含了(100000,000100,000010,000001,100100,100011……)这些数,然后取两个最大值。

100011这个答案包含了(100001,100000,00001)。所以复杂度很小。类似于前缀最大。

注意答案的两个数不能重复,所以我们记录答案的时候只记录下标,这样就很容易判重

#include <iostream>
#include <stdio.h>
using namespace std;
#define LL long long
const LL maxn = (1<<18)+22;

int a[maxn];

struct node
{
	int a,b;
}Max[maxn];

void f(int x,node &b)
{
	if(b.a==x||b.b==x) return;
	if(a[b.a]<a[x]) swap(b.a,b.b),b.a=x;
	else if(a[b.b]<a[x]) b.b=x;
}


int main()
{
	int n;
	scanf("%d",&n);
	int N=1<<n;
	for(int i=0;i<N;i++){
		scanf("%d",&a[i]);
		Max[i].a=i;
		Max[i].b=N;
	}
	for(int i=1;i<N;i++){
		for(int j=0;j<n;j++){
			if(i&(1<<j)){
				int k=i^(1<<j);
				f(Max[k].a,Max[i]);
				f(Max[k].b,Max[i]);
			}
		}
	}
	int ans=0;
	for(int i=1;i<N;i++){
		ans=max(ans,a[Max[i].a]+a[Max[i].b]);
		printf("%d\n",ans);
	}


	return 0;
}

猜你喜欢

转载自blog.csdn.net/mr_treeeee/article/details/81109181
今日推荐