Loading

P3262 [JLOI2015]战争调度 - 树形 dp,状压 dp

题解

由于这是一棵满二叉树,所以其树高只有 \(n\)

\(f_{i,j,S}\)\(i\) 的子树内,有 \(j\) 个平民参战,且 \(\operatorname{fa}(i)\sim 1\) 的路径上的点,颜色为 \(S\) 的对应二进制位。转移就是普通的树上背包。

乍一看时间复杂度不太对,但 \(f\) 只有 \(\sum_{i=1}^n 2^i\times 2^{n-i}\times 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\n",ans);
	return 0;
}
posted @ 2021-11-01 18:39  Alan_Zhao_2007  阅读(44)  评论(0编辑  收藏  举报