当前位置: 首页 > news >正文

题解:P8294 [省选联考 2022] 最大权独立集问题

题意

给定一棵 \(n\) 个点的二叉树,点 \(i\) 的点权为 \(a_i\)。你需要将所有边依次删除,删除一条边 \((u,v)\) 会交换 \(a_u\)\(a_v\),并产生 \(a_u+a_v\) 的代价,求最小代价。\(1\leq n\leq 5\times 10^3\)

题解

构式题。

考虑到对于一棵子树 \(sub_x\),其能和子树外产生的联系只会是:操作 \((x,fa_x)\) 这条边,使得 \(x\) 上的点权 \(a_u\) 交换到子树外,\(fa_x\) 上的某个点权交换到子树内。设计这样的 DP 状态:对于 \(u,v\),设 \(x=\operatorname{lca}(u,v)\),则 \(f_{u,v}\) 表示考虑 \(sub_x\) 内的点,将 \(a_u\) 换到子树外,将换入的点权一路交换到 \(v\),要求把 \(sub_x\) 内所有边和 \((x,fa_x)\) 这条边删去的最小代价。注意我们在状态中不考虑换入点权产生的代价,可以理解为一种延后计算,在更浅的祖先处再去计算这个代价。

自底向上做树形 DP,DFS 到点 \(x\) 时计算所有 \(\operatorname{lca}(u,v)=x\)\(f_{u,v}\)

接下来是一些精神污染的部分,我们分类讨论 \(x\) 的儿子节点个数。

\(|son_x|=0\)

显然 \(f_{x,x}=a_x\)


\(|son_x|=1\)

此时有 \(u=x\)\(v=x\)。设 \(x\) 的儿子为 \(y\)

\(u=x\)

考虑 \(x\) 的邻边的操作先后顺序,容易发现是先操作 \((x,fa_x)\) 再操作 \((x,y)\)。枚举 \(i\) 使得 \(\operatorname{lca}(i,v)=y\)

\[f_{u,v}\gets\min\{f_{i,v}+a_u\} \]

\(v=x\)

此时是先操作 \((x,y)\) 再操作 \((x,fa_x)\)。枚举 \(i\) 使得 \(\operatorname{lca}(u,i)=y\),则操作完 \((x,y)\) 后,\(a_v\) 会作为 \(y\) 子树的换入点权,需要加上这部分贡献,有转移:

\[f_{u,v}\gets\min\{f_{u,i}+a_u+a_v(dep_i-dep_v)\} \]


\(|son_x|=2\)

\(u\neq x\land v\neq x\)

\(x\)\(u\) 方向上的儿子为 \(y_1\),另一侧的儿子为 \(y_2\)。此时是先操作 \((x,y_1)\),再操作 \((x,fa_x)\),最后再操作 \((x,y_2)\)。枚举 \(i,j\) 使得 \(\operatorname{lca}(u,i)=y_1,\operatorname{lca}(j,v)=y_2\),则操作完 \((x,y_1)\) 后,\(a_x\) 会作为 \(y_1\) 子树的换入点权,有转移:

\[f_{u,v}\gets \min\{f_{u,i}+f_{j,v}+a_x(dep_i-dep_x)+a_u\} \]

对于每个 \(u\),预处理出 \(\min\limits_i\{f_{u,i}+a_x(dep_i-dep_x)+a_u\}\),对于每个 \(v\),预处理出 \(\min\limits_j f_{j,v}\)。枚举 \(u,v\) 转移即可。

\(u=x\)

\(x\)\(v\) 方向上的儿子为 \(y_1\),另一侧的儿子为 \(y_2\)。此时是先操作 \((x,fa_x)\),再操作 \((x,y_1)\),最后再操作 \((x,y_2)\)。枚举 \(i,j,k\) 使得 \(\operatorname{lca}(i,v)=y_1,\operatorname{lca}(j,k)=y_2\),则操作完 \((x,y_1),(x,y_2)\) 后,\(a_i\) 会作为 \(y_2\) 子树的换入点权,有转移:

\[\begin{align*} f_{u,v}\gets&\min\{f_{i,v}+f_{j,k}+a_i(dep_k-dep_u)+a_u\}\\ =&\min_i\left\{f_{i,v}+\min_k\left\{a_i(dep_k-dep_u)+\min_j f_{j,k}\right\}\right\}+a_u \end{align*} \]

对于每个 \(k\),预处理出 \(h_k=\min\limits_j f_{j,k}\),再对每个 \(i\),预处理出 \(g_i=\min\limits_k\{a_i(dep_k-dep_u)+h_k\}\),枚举 \(u,v,i\) 转移即可。每对 \((j,k)\) 只会在 \(y_2\) 处被枚举到,每对 \((v,i)\) 只会在 \(y_1\) 处被枚举到,时间复杂度为 \(\mathcal{O}(n^2)\)

\(v=x\)

\(x\)\(u\) 方向上的儿子为 \(y_1\),另一侧的儿子为 \(y_2\)。此时是先操作 \((x,y_2)\),再操作 \((x,y_1)\),最后再操作 \((x,fa_x)\)。枚举 \(i,j,k\) 使得 \(\operatorname{lca}(u,i)=y_1,\operatorname{lca}(j,k)=y_2\),则操作完 \((x,y_2)\) 后,\(a_v\) 会作为 \(y_2\) 子树的换入点权,再操作 \((x,y_1)\) 后,\(a_j\) 会作为 \(y_1\) 子树的换入点权,有转移:

\[\begin{align*} f_{u,v}\gets&\min\{f_{u,i}+f_{j,k}+a_v(dep_k-dep_v)+a_j(dep_i-dep_v)+a_u\}\\ =&\min_i\left\{f_{u,i}+\min_j\left\{a_j(dep_i-dep_v)+\min_k\{f_{j,k}+a_v(dep_k-dep_v)\}\right\}\right\}+a_u \end{align*} \]

对于每个 \(j\),预处理出 \(h_j=\min\limits_k\{f_{j,k}+a_v(dep_k-dep_v)\}\),再对每个 \(i\),预处理出 \(g_i=\min\limits_j\{a_j(dep_i-dep_v)+h_j\}\),枚举 \(u,v,i\) 转移即可。时间复杂度为 \(\mathcal{O}(n^2)\)


考虑如何统计答案,还是分类讨论 \(1\) 号节点的儿子个数。

\(|son_1|=1\)

\(son_1=y\)。枚举 \(u,v\) 使得 \(\operatorname{lca}(u,v)=y\),则操作完 \((1,y)\)\(a_1\) 会作为 \(y\) 子树的换入点权:

\[ans\gets \min\{f_{u,v}+a_1(dep_v-dep_1)\} \]

\(|son_1|=2\)

\(son_1=\{y_1,y_2\}\),不妨钦定我们是先操作 \((1,y_1)\) 再操作 \((1,y_2)\)。枚举 \(i,j,k,l\) 使得 \(\operatorname{lca}(i,j)=y_1,\operatorname{lca}(k,l)=y_2\),则操作完 \((1,y_1)\) 后,\(a_1\) 会作为 \(y_1\) 子树的换入点权,再操作完 \((1,y_2)\) 后,\(a_i\) 会作为 \(y_2\) 子树的换入点权:

\[ans\gets \min\{f_{i,j}+f_{k,l}+a_1(dep_j-dep_1)+a_i(dep_l-dep_1)\} \]

对于每个 \(i\),预处理出 \(\min\limits_j\{f_{i,j}+a_1(dep_j-dep_1)\}\),对于每个 \(l\),预处理出 \(\min\limits_k f_{k,l}\),枚举 \(i,l\) 更新答案即可。


这样我们就 \(\mathcal{O}(n^2)\) 地解决了本题。

代码倒是不算难写。

主要代码
void dfs1(int x) {sub[x].push_back(x);for (int y : son[x]) {dep[y] = dep[x] + 1, dfs1(y);for (int z : sub[y]) sub[x].push_back(z);}for (int y : sub[x]) vec[x].push_back({x, y}), vec[x].push_back({y, x});if (son[x].size() == 2) {int y0 = son[x][0], y1 = son[x][1];for (int p : sub[y0]) for (int q : sub[y1])vec[x].push_back({p, q}), vec[x].push_back({q, p});}
}
void dfs2(int x) {if (son[x].empty()) return f[x][x] = a[x], void();for (auto y : son[x]) dfs2(y);if (son[x].size() == 1) {int s = son[x][0];for (auto [v, i] : vec[s]) {chk_min(f[x][v], f[i][v] + a[x]);int u = v;chk_min(f[u][x], f[u][i] + (ll)a[x] * (dep[i] - dep[x]) + a[u]);}return;}int y0 = son[x][0], y1 = son[x][1];for (int y : sub[x]) mn1[y] = mn2[y] = inf;for (auto [j, k] : vec[y0]) chk_min(mn1[k], f[j][k]);for (auto [j, k] : vec[y1]) chk_min(mn1[k], f[j][k]);for (auto [i, k] : vec[x]) if (i != x && k != x)chk_min(mn2[i], (ll)a[i] * (dep[k] - dep[x]) + mn1[k]);for (auto [v, i] : vec[y0]) chk_min(f[x][v], a[x] + f[i][v] + mn2[i]);for (auto [v, i] : vec[y1]) chk_min(f[x][v], a[x] + f[i][v] + mn2[i]);for (int y : sub[x]) mn1[y] = mn2[y] = inf;for (auto [j, k] : vec[y0]) chk_min(mn1[j], f[j][k] + (ll)a[x] * (dep[k] - dep[x]));for (auto [j, k] : vec[y1]) chk_min(mn1[j], f[j][k] + (ll)a[x] * (dep[k] - dep[x]));for (auto [i, j] : vec[x]) if (i != x && j != x)chk_min(mn2[i], (ll)a[j] * (dep[i] - dep[x]) + mn1[j]);for (auto [u, i] : vec[y0]) chk_min(f[u][x], a[u] + f[u][i] + mn2[i]);for (auto [u, k] : vec[y1]) chk_min(f[u][x], a[u] + f[u][k] + mn2[k]);for (int y : sub[x]) mn1[y] = mn2[y] = inf;for (auto [u, i] : vec[y0]) {chk_min(mn1[u], f[u][i] + (ll)a[x] * (dep[i] - dep[x]) + a[u]);int j = u, v = i;chk_min(mn2[v], f[j][v]);}for (auto [u, i] : vec[y1]) {chk_min(mn1[u], f[u][i] + (ll)a[x] * (dep[i] - dep[x]) + a[u]);int j = u, v = i;chk_min(mn2[v], f[j][v]);}for (auto [u, v] : vec[x]) if (u != x && v != x)chk_min(f[u][v], mn1[u] + mn2[v]);
}int main() {ios::sync_with_stdio(0), cin.tie(0);cin >> n;for (int i = 1; i <= n; ++i) cin >> a[i];for (int i = 2; i <= n; ++i) cin >> fa[i], son[fa[i]].push_back(i);for (int i = 1; i <= n; ++i) fill(f[i] + 1, f[i] + n + 1, inf);dfs1(1);for (int y : son[1]) dfs2(y);if (son[1].size() == 1) {for (auto [u, v] : vec[son[1][0]])chk_min(ans, f[u][v] + (ll)a[1] * (dep[v] - dep[1]));} else {for (int y : sub[1]) mn1[y] = mn2[y] = inf;int y0 = son[1][0], y1 = son[1][1];for (auto [i, j] : vec[y0]) chk_min(mn1[i], f[i][j] + (ll)a[1] * (dep[j] - dep[1]));for (auto [k, l] : vec[y1]) chk_min(mn2[l], f[k][l]);for (auto [i, l] : vec[1]) if (i != 1 && l != 1)chk_min(ans, mn1[i] + mn2[l] + (ll)a[i] * (dep[l] - dep[1]));for (int y : sub[1]) mn1[y] = mn2[y] = inf;swap(y0, y1);for (auto [i, j] : vec[y0]) chk_min(mn1[i], f[i][j] + (ll)a[1] * (dep[j] - dep[1]));for (auto [k, l] : vec[y1]) chk_min(mn2[l], f[k][l]);for (auto [i, l] : vec[1]) if (i != 1 && l != 1)chk_min(ans, mn1[i] + mn2[l] + (ll)a[i] * (dep[l] - dep[1]));}cout << ans;return 0;
}
http://www.jsqmd.com/news/318364/

相关文章:

  • 大数据深度学习|计算机毕设项目|计算机毕设答辩|路口视频的车辆运动方向监测系统
  • 大数据深度学习|计算机毕设项目|计算机毕设答辩|排水管道病害智能辨识算法研究
  • 内卷不如内建!7天实战:从零开始构建大模型Agent,小白也能成为AI编程高手
  • 在Windows的WSL中试用GizmoSQL UI连接GizmoSQL数据库服务器
  • 【区块链学习笔记】17:以太坊中的GHOST协议 - 教程
  • 2026年花岗岩路沿石厂家推荐:五莲红路沿石、黄金麻干挂石材、中国黑干挂石材、大理石路沿石、芝麻白路沿石、五莲花路沿石选择指南
  • vulnstack1
  • 2026年创意巴士广告厂家权威推荐榜:车身广告安装/创意巴士广告/车衣广告/创意大巴广告/改装车广告/痛车/路演车广告/选择指南
  • C++ 枚举(enum)与联合体(union)全解析——从内存到底层机制全面掌握 - 详解
  • 2026年蔡司三坐标供应商盘点:浙江地区代理商与经销商联系方式汇总
  • 2026年车身广告安装厂家推荐:车身广告安装/创意巴士广告/车衣广告/创意大巴广告/改装车广告/痛车/路演车广告/选择指南
  • 收藏!2026年AI开发者必学:上下文工程6大核心组件决定应用75%质量
  • 2026国内最新家装品牌top5推荐!金牛区/新都区等地优质家装公司权威榜单发布,品质工艺双优助力理想家居生活.
  • Excel的终结者来了:Claude for Excel深度解析,收藏这份AI办公革命指南
  • 现代汽车“让每一天都充满传奇”
  • 【干货收藏】企业AI Agent实战指南:从技术原理到工程落地,避坑指南
  • AI开发者的厨房秘籍:RAG、Agent、MCP、Skill一篇文章全搞懂!
  • 2026执医技能考!就选阿虎医考“技能小黑屋”
  • 基于遗传算法优化的RBF神经网络优化算法代码实现(MATLAB版)
  • 2026国内最新房屋装修咨询top5推荐!金牛区/新都区等地优质家装公司权威榜单发布,资质服务双优助力品质家居生活
  • 【C语言】 memcpy (数据复制)
  • 北京丰宝斋上门回收老书古书,藏家闲置变现认准老字号更靠谱
  • 处理前端Pending请求:表单防重复与搜索防抖
  • 阿里云服务器无法拉取DockerHub镜像(无代理)解决方案(附完整命令+步骤)(第一次部署开源项目)
  • 全网热议!2026年知名砖机品牌推荐前10款,帮你提升生产效率
  • 【收藏】从零构建高效RAG知识库:文档处理不能一概而论,这些技巧你必须知道
  • 执业医师网课机构,我为什么推荐阿虎医考
  • AI能干的秘密:Function Calling、MCP与Agent Skills三件套,收藏级深度解析
  • 备考临床执业医师?阿虎医考值得你关注!
  • 大数据深度学习|计算机毕设项目|计算机毕设答辩|基于图像识别的桥梁裂缝识别与检测系统设计与实现