ATCODER:Or Plus Max(二进制递推)

E - Or Plus Max


Time limit : 2sec / Memory limit : 1024MB

Score : 700 points

Problem Statement

There is an integer sequence of length 2NA0,A1,…,A2N−1. (Note that the sequence is 0-indexed.)

For every integer K satisfying 1≤K≤2N−1, solve the following problem:

  • Let i and j be integers. Find the maximum value of Ai+Aj where 0≤i<j≤2N−1 and (i or j)≤K. Here, or denotes the bitwise OR.

Constraints

  • 1≤N≤18
  • 1≤Ai≤109
  • All values in input are integers.

Input

Input is given from Standard Input in the following format:

N
A0 A1  A2N−1

Output

Print 2N−1 lines. In the i-th line, print the answer of the problem above for K=i.


Sample Input 1

Copy
2
1 2 3 1

Sample Output 1

Copy
3
4
5

For K=1, the only possible pair of i and j is (i,j)=(0,1), so the answer is A0+A1=1+2=3.

For K=2, the possible pairs of i and j are (i,j)=(0,1),(0,2). When (i,j)=(0,2)Ai+Aj=1+3=4. This is the maximum value, so the answer is 4.

For K=3, the possible pairs of i and j are (i,j)=(0,1),(0,2),(0,3),(1,2),(1,3),(2,3) . When (i,j)=(1,2)Ai+Aj=2+3=5. This is the maximum value, so the answer is 5.


Sample Input 2

Copy
3
10 71 84 33 6 47 23 25

Sample Output 2

Copy
81
94
155
155
155
155
155

Sample Input 3

Copy
4
75 26 45 72 81 47 97 97 2 2 25 82 84 17 56 32

Sample Output 3

Copy
101
120
147
156
156
178
194
194
194
194
194
194
194
194
194
题意:给2的n次方个数你,对于其中每个下标k,找出两个下标按位或<=k的且和最大的输出。

思路:或之值<=k,显然也满足<=k-1,那么只需要考虑或之值=k的就行,因为<k的可以有k-1推出来。那么显然问题变成找出k的二进制中两个子集(i和j)相加的和最大,这里子集意思是i or j or k = k且i不等于j。那么怎么算呢?比如k(2) = 1011,那么我们轮流将其中的某个1换成0,从得出新的那个数转移两个值过来,即维护最大的两个值,就能做到不缺漏了。复杂度O(N*2^N)

# include <bits/stdc++.h>
# define A first
# define B second
# define PII pair<int,int>
using namespace std;
const int maxn = 1<<18;
int a[maxn+3];
PII imax[maxn];
void fun(int x, PII &y)
{
    if(x == y.A || x == y.B) return;
    if(a[x] > a[y.A]) y.B = y.A,y.A = x;
    else if(a[x] > a[y.B]) y.B=x;
}
int main()
{
    int N, n, ans=0;
    scanf("%d",&n);
    N = 1<<n;
    a[N] = 0;
    for(int i=0; i<N; ++i) scanf("%d",&a[i]), imax[i].A=i, imax[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);
                fun(imax[k].A, imax[i]);
                fun(imax[k].B, imax[i]);
            }
        }
    }
    for(int i=1; i<N; ++i)
    {
        ans = max(ans, a[imax[i].A]+a[imax[i].B]);
        printf("%d\n",ans);
    }
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值