ARC100E题解
垃圾 \(O(3^n)\) 做法/kk
对于每个 \(k\) 分别计算答案,注意到 \(i\) 一定是 \(k\) 的子集所以先枚举一个 \(i\),此时 \(j\) 应该是被钦定 \(i\) 为 \(1\) 的部分为 \(0\),剩下 \(k\) 的子集部分可以随意取 \(0/1\)。
于是弄一个类似前缀和的东西 \(f[S]\),\(S\) 的第 \(i\) 位为 \(0/1/2\) 表示这一位被 钦定为 \(0/1\)/都可以,这个东西随便转移一下就好了,容易发现复杂度为 \(O(3^n)\)。
总复杂度 \(O(3^n)\),被 \(O(n2^n)\) 吊起来锤/kk
容易发现这个东西是非常容易被薄纱的,来考虑一个很神奇的性质:
若 \(i|j\subseteq k\),那么 \(i|j\leq k\)。
根据这个性质,对每个 \(k\) 分别计算答案的时候可以考虑计算 \(i|j\subseteq k\)。
也就是说我们可以计算 \(k\) 子集中的最大值和次大值,然后将其加起来。
计算方法和 FWT 还是一样的,按位考虑即可。
复杂度 \(O(n2^n)\)。
#include<cstdio>
#include<cctype>
const int M=1<<18;
int n;
inline int max(const int&a,const int&b){
return a>b?a:b;
}
struct data{
int mx1,mx2;
data(const int&mx1=0,const int&mx2=0):mx1(mx1),mx2(mx2){}
inline data operator+(const data&it){
return mx1==it.mx1?data(mx1,mx1):mx1>it.mx1?data(mx1,max(mx2,it.mx1)):data(it.mx1,max(mx1,it.mx2));
}
inline data&operator+=(const data&it){
return*this=*this+it;
}
}f[M];
inline int read(){
int n(0);char s;while(!isdigit(s=getchar()));while(n=n*10+(s&15),isdigit(s=getchar()));return n;
}
inline void write(int n){
static char s[15];int top(0);while(s[++top]=n%10^48,n/=10);while(putchar(s[top]),--top);putchar(10);
}
signed main(){
n=read();for(int i=0;i<(1<<n);++i)f[i]=data(read(),0);
for(int len=1;len<(1<<n);len<<=1)for(int k=0;k<(1<<n);k+=len<<1)for(int i=0;i<len;++i)f[i|k|len]+=f[i|k];
for(int mx(f[0].mx1),i=1;i<(1<<n);++i)write(mx=max(mx,f[i].mx1+f[i].mx2));
}