题意
给定一棵高度为 (n) 的完全二叉树,可以将节点设置成两种状态。如果某个叶子 (x) 的状态为 (i) 同时他的某个祖先也为 (i),那么这个叶子就会对祖先产生 (f_{x,i}) 的贡献。求叶子状态为 (0) 的数量小于等于 (m) 的最大贡献。
( exttt{Data Range:}1leq nleq 10,mleq 2^{n-1})
题解
考虑先设一个 (f_{i,j}) 表示到了 (i) 点,叶子选了 (j) 个 (0) 的,这个子树对祖先的总贡献,但是会发现这个东西好像做不了。
于是考虑状压。设 (f_{i,j,S}) 表示到了 (i) 点,叶子选了 (j) 个 (0),这个点的祖先的选择的状态为 (S) 时这个字数对祖先的总贡献。
然后这个东西就做个树形背包就好了。
实现的时候可以考虑爆搜一下,就可以压缩掉 (S) 这一维。
代码
#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=2051;
ll n,m,sz,res;
ll u[MAXN][15],v[MAXN][15],f[MAXN][MAXN<<1];
inline ll read()
{
register ll num=0,neg=1;
register char ch=getchar();
while(!isdigit(ch)&&ch!='-')
{
ch=getchar();
}
if(ch=='-')
{
neg=-1;
ch=getchar();
}
while(isdigit(ch))
{
num=(num<<3)+(num<<1)+(ch-'0');
ch=getchar();
}
return num*neg;
}
#define ls node<<1
#define rs node<<1|1
inline void dfs(ll node,ll sz,ll st)
{
for(register int i=0;i<=sz;i++)
{
f[node][i]=0;
}
if(sz==1)
{
for(register int i=0;i<n-1;i++)
{
if(st&(1<<i))
{
f[node][1]+=u[node][i+1];
}
else
{
f[node][0]+=v[node][i+1];
}
}
return;
}
for(register int i=0;i<2;i++)
{
dfs(ls,sz>>1,st<<1|i),dfs(rs,sz>>1,st<<1|i);
for(register int j=0;j<=min(sz,m);j++)
{
for(register int k=0;k<=min(sz,m);k++)
{
f[node][j+k]=max(f[node][j+k],f[ls][j]+f[rs][k]);
}
}
}
}
int main()
{
n=read(),m=read(),sz=1<<n;
for(register int i=sz>>1;i<sz;i++)
{
for(register int j=1;j<=n-1;j++)
{
u[i][j]=read();
}
}
for(register int i=sz>>1;i<sz;i++)
{
for(register int j=1;j<=n-1;j++)
{
v[i][j]=read();
}
}
dfs(1,sz-1,0);
for(register int i=0;i<=m;i++)
{
res=max(res,f[1][i]);
}
printf("%d
",res);
}