题解
由于这是一棵满二叉树,所以其树高只有 (n)。
设 (f_{i,j,S}) 为 (i) 的子树内,有 (j) 个平民参战,且 (operatorname{fa}(i)sim 1) 的路径上的点,颜色为 (S) 的对应二进制位。转移就是普通的树上背包。
乍一看时间复杂度不太对,但 (f) 只有 (sum_{i=1}^n 2^i imes 2^{n-i} imes 2^i) 个状态;一个状态 ((i,j,S)) 的转移复杂度是 (mathcal{O}(j))。于是总复杂度是 (mathcal{O}(n4^n))。
但这样 (f) 比较难存储,发现可以把 (S) 通过 dfs 砍掉。
Bonus:应该可以通过上下界优化,让时间复杂度变成 (mathcal{O}(4^n))?
代码
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
template<typename T>
void Read(T &_x){
_x=0;int _f=1;
char ch=getchar();
while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar();
_x*=_f;
}
template<typename T,typename... Args>
void Read(T &_x,Args& ...others){
Read(_x);Read(others...);
}
typedef long long ll;
const int Vertex=1030;
int n,m,son[Vertex][2],fa[Vertex],dep[Vertex],cont[Vertex][Vertex][2];
ll f[Vertex][Vertex];
void Dfs(int u,int stat){
memset(f[u],0,sizeof(f[u]));
if(!son[u][0]){
for(int v=fa[u],st=stat;v;v=fa[v],st>>=1){
if(st&1) f[u][1]+=cont[u][v][1];
else f[u][0]+=cont[u][v][0];
}
return;
}
For(i,0,1){
Dfs(son[u][0],stat<<1|i);
Dfs(son[u][1],stat<<1|i);
int mx=(1<<(n-dep[u]));
For(j,0,min(mx,m)){
For(k,0,j){
f[u][j]=max(f[u][j],f[son[u][0]][k]+f[son[u][1]][j-k]);
}
}
}
}
int main(){
Read(n,m);
For(i,1,(1<<(n-1))-1) son[i][0]=i<<1,son[i][1]=i<<1|1;
For(i,1,(1<<n)-1) fa[i]=i/2,dep[i]=dep[fa[i]]+1;
For(i,1,1<<(n-1)){
int u=(1<<(n-1))+i-1,v=fa[u];
For(j,1,n-1){
int x;Read(x);
cont[u][v][1]=x;
v=fa[v];
}
}
For(i,1,1<<(n-1)){
int u=(1<<(n-1))+i-1,v=fa[u];
For(j,1,n-1){
int x;Read(x);
cont[u][v][0]=x;
v=fa[v];
}
}
Dfs(1,0);
ll ans=0;
For(i,0,m) ans=max(ans,f[1][i]);
printf("%lld
",ans);
return 0;
}