给出长度为 的两个序列 和 ,求一个长度为 的序列 ,满足 。
。
考虑枚举 ,不难发现, 为 的二进制位 都能随便选。那么只要用某种方法求出 表示 为 的二进制位可以乱选且其他二进制位和 一致的 中最大的 ,就可以更新 。
不难发现,只要在枚举 的第 位为 时,枚举 的补集的子集 ,令 即让这一位为 的 加上这一位为 的 的贡献就可以快速维护 。
维护 的时间复杂度是 的,更新答案的时间复杂度也是 的,足以通过此题。
代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
const int S=300005;
int k,n;
long long a[S],b[S];
long long mx[20][S],ans[S];
void slove(int pos,int cnt,int sta)
{
int inv=n-1^sta;
if(pos==k)
{
for(int T=inv;;T=inv&(T-1))
{
ans[T|sta]=max(ans[T|sta],a[sta]+mx[cnt][T|sta]);
if(T==0) break;
}
return;
}
slove(pos+1,cnt,sta);
sta|=1<<pos;
inv=n-1^sta;
for(int T=inv;;T=inv&(T-1))
{
mx[cnt+1][T|sta]=max(mx[cnt][T|sta],mx[cnt][T|sta^(1<<pos)]);
if(T==0) break;
}
slove(pos+1,cnt+1,sta);
}
int main()
{
scanf("%d",&k);
n=1<<k;
for(int i=0;i<=n-1;i++) scanf("%lld",&a[i]);
for(int i=0;i<=n-1;i++) scanf("%lld",&b[i]),mx[1][i]=b[i];
slove(0,1,0);
for(int i=0;i<=n-1;i++) printf("%lld ",ans[i]);
printf("\n");
return 0;
}