luogu-P3262题解

简要题意

有一棵不超过十层的满二叉树,需要对每个节点进行染色。每个叶子节点会对其颜色相同的祖先节点产生贡献且黑白贡献不同。求最大贡献。

题解

首先我会暴力!我如果直接暴力枚举每个节点的颜色,复杂度就是 O(22n)。然后还要算贡献,所以还有一个 O(2n1(n1))。然后肯定爆了。

不用说都知道是因为我们在枚举的时候重复枚举了很多相同的状态,这时我们应该单独考虑贡献。对于一个点,它的贡献应该只与其祖先节点有关,而我们暴力是去枚举了其他无关的节点。所以我们就可以设计一个 fu,S 表示当前考虑到 u 节点,u 的祖先状态为 S 时子树最大贡献。然后题目中还有对黑点的个数限制,所以我们还需要加一维表示子树内黑点个数。所以最终状态是 fu,S,cnt 表示 u 的子树内,其祖先染色状态为 S 且子树内有 cnt 个黑点时最大贡献。然后对于任意节点,假设它在第 x 层,则它的祖先结点状态数为 2x1,子树大小为 2nx+11,所以时间复杂度为 O(n×2n)。但是空间是 O(n×22n),所以我们就在搜索状态的过程中记录一个状态 S,数组就只用开二维。

转移其实就是合并两个子树的信息,所以直接预处理出叶子节点的答案,然后跑一遍 dfs 然后类似合并背包一样做即可。

代码

inline void upd(int &x, int y){x = x > y ? x : y;}

void dfs(int u, int sta, int dep){
	if(dep == n)return(void)(f[u][0] = c[u - lim][sta][0], f[u][1] = c[u - lim][sta][1]);
	for(int i = 0; i <= qp[dep]; ++i)f[ls][i] = f[rs][i] = 0;
	dfs(ls, sta, dep + 1); dfs(rs, sta, dep + 1);
	for(int i = 0; i <= qp[dep]; ++i)for(int j = 0; j <= qp[dep]; ++j)upd(f[u][i + j], f[ls][i] + f[rs][j]);
	for(int i = 0; i <= qp[dep]; ++i)f[ls][i] = f[rs][i] = 0;
	dfs(ls, sta + qp[dep], dep + 1), dfs(rs, sta + qp[dep], dep + 1);
	for(int i = 0; i <= qp[dep]; ++i)for(int j = 0; j <= qp[dep]; ++j)upd(f[u][i + j], f[ls][i] + f[rs][j]);
}
signed main(){
	n = rd(), m = rd(); lim = 1 << n - 1;
	for(int i = 0; i < lim; ++i)for(int j = 0; j < n - 1; ++j)v[i][j][1] = rd();
	for(int i = 0; i < lim; ++i)for(int j = 0; j < n - 1; ++j)v[i][j][0] = rd();
	qp[n - 1] = 1; for(int i = n - 2; ~ i; --i)qp[i] = qp[i + 1] << 1;
	for(int i = 0; i < lim; ++i)for(int j = 0; j < lim; ++j)
		for(int k = 0, bit = i; k < n - 1; ++k, bit >>= 1)c[j][i][bit & 1] += v[j][k][bit & 1];
	dfs(1, 0, 1); int ans = 0;
	for(int i = 0; i <= m; ++i)upd(ans, f[1][i]);
	wt(ans);
}
posted @   Lyrella  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示