P2495 [SDOI2011]消耗战(树形dp+虚树)

P2495 [SDOI2011]消耗战

树形dp
状态表示:fuf_ufu表示以uuu为根的子树中,uuu节点与子树中的关键的“隔开”所需要的最小代价
状态转移:
考虑uuu的一个儿子vvv

  • vvv是关键点:fu=fu+wu→vf_u=f_u+w_{u\to v}fu=fu+wuv
  • vvv不是关键的:fu=fu+min⁡(wu→v,fv)f_u=f_u+\min(w_{u\to v},f_v)fu=fu+min(wuv,fv)

于是有下面暴力代码O(nm+∑k)O(nm+\sum k)O(nm+k)

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
//#pragma GCC optimize(2)
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
using ll=long long;
constexpr int N=250010;
int h[N],e[2*N],ne[2*N],w[2*N],idx;
void add(int a,int b,int c){e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;}
ll f[N];
int n,m;
bool mp[N];
void dfs(int u,int fa)
{for(int i=h[u];i!=-1;i=ne[i]){int v=e[i];if(v==fa) continue;dfs(v,u);if(mp[v]) f[u]+=w[i];elsef[u]+=min((ll)w[i],f[v]);}
}
int main()
{IO;cin>>n;memset(h,-1,sizeof h);for(int i=1;i<n;i++){int a,b,c;cin>>a>>b>>c;add(a,b,c),add(b,a,c);}cin>>m;while(m--){memset(f,0ll,sizeof(ll)*(n+1));memset(mp,0,sizeof(bool)*(n+1));int k;cin>>k;while(k--){int p;cin>>p;mp[p]=1;}dfs(1,0);cout<<f[1]<<'\n';}return 0;
}

虚树-树上动态规划的利器
oiwiki虚树

虚树:根据原树构建一颗虚拟的树,这棵树只包含{\color{red}只包含}关键节点以及关键节点的最近公共祖先(LCA)

构建过程:

  • 将关键节点的时间戳排序
  • 用一个栈维护一条虚树上的链,根节点到当前关键节点,将关键点依次push进栈中

push过程:
大佬图片非常清晰
下面用top表示栈顶元素,cur表示当前需要插入的节点
anc=lca(top,cur)
首先如果说anc=top,说明cur应该接在栈顶后面即可
在这里插入图片描述

否则会出现下面情况

在这里插入图片描述
当前栈中维护的是绿色{\color{green}绿色}绿的那一条链,我们需要让当前栈维护从根节点到当前节点即蓝色{\color{blue}蓝色}那条链,只需要让top-1向top连边,并且不断top- -即可。

最后需要判断是否存在下面情况
如果存在,需要连一条anc到top的边,然后弹出top- -,并将anc入栈
在这里插入图片描述

代码如下

void insert(int u)
{int anc=lca(u,stk[tt]);while(tt>1&&dfn[stk[tt-1]]>=dfn[anc])E[stk[tt-1]].push_back(stk[tt]),tt--;if(stk[tt]!=anc) E[anc].push_back(stk[tt]),stk[tt]=anc;//最后的情况stk[++tt]=u;
}
void build()// 构建虚树
{sort(is+1,is+1+m,[](const int &a,const int &b){return dfn[a]<dfn[b];});//按照dfn排序stk[tt=1]=1;//根节点for(int i=1;i<=m;i++) insert(is[i]);while(tt) E[stk[tt-1]].push_back(stk[tt]),tt--;// 连边
}

显然虚树两点(u,v)之间边u→vu\to vuv的大小应为原树中路径min⁡(u⇝v)\min(u \leadsto v)min(uv),倍增求lca过程即可求出边权。

而下面代码采取另一种做法:
首先预处理原树中根节点到当前节点路径的最小值
dpudp_udpu表示从u开始不能到达其子树中的关键点所需切断的最小边权和。
切断儿子vvv要么用mn[v]mn[v]mn[v]要么切断子树,如果当前节点是关键节点,必须切断当前节点即花费代价为mn[u]mn[u]mn[u]

注意:虚树清空节点需要在dfs过程中清空,不能使用memset

时间复杂度∑klog⁡n\sum k\log nklogn

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
//#pragma GCC optimize(2)
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
using ll=long long;
constexpr int N=250010;
int h[N],e[2*N],ne[2*N],w[2*N],idx;
void add(int a,int b,int c){e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;}int dep[N],sz[N],son[N],fa[N];
int mn[N];
void dfs1(int u)
{sz[u]=1;for(int i=h[u];i!=-1;i=ne[i]){int v=e[i];if(v==fa[u]) continue;fa[v]=u;dep[v]=dep[u]+1;mn[v]=min(mn[u],w[i]);dfs1(v);sz[u]+=sz[v];if(sz[son[u]]<sz[v]) son[u]=v;}
}
int top[N],dfn[N],timestamp;
void dfs2(int u,int t)
{dfn[u]=++timestamp;top[u]=t;if(!son[u]) return;dfs2(son[u],t);for(int i=h[u];i!=-1;i=ne[i]){int v=e[i];if(v==fa[u]||v==son[u]) continue;dfs2(v,v);}
}
int lca(int u,int v)
{while(top[u]!=top[v]){if(dep[top[u]]<dep[top[v]]) swap(u,v);u=fa[top[u]];}return dep[u]<dep[v]?u:v;
}
//====================================================树剖求lca
int n,m;
int stk[N],tt;
int is[N];
bool mp[N];
vector<int> E[N];
void insert(int u)
{int anc=lca(u,stk[tt]);while(tt>1&&dfn[stk[tt-1]]>=dfn[anc])E[stk[tt-1]].push_back(stk[tt]),tt--;if(stk[tt]!=anc) E[anc].push_back(stk[tt]),stk[tt]=anc;stk[++tt]=u;
}
void build()// 构建虚树
{sort(is+1,is+1+m,[](const int &a,const int &b){return dfn[a]<dfn[b];});//按照dfn排序stk[tt=1]=1;//根节点for(int i=1;i<=m;i++) insert(is[i]);while(tt) E[stk[tt-1]].push_back(stk[tt]),tt--;
}
ll dfs3(int u)
{ll cost=0;for(int v:E[u]) cost+=min((ll)mn[v],dfs3(v));E[u].clear();if(mp[u]) return mn[u];else return cost;
}
int main()
{IO;cin>>n;memset(h,-1,sizeof h);memset(mn,0x3f,sizeof mn);for(int i=1;i<n;i++){int a,b,c;cin>>a>>b>>c;add(a,b,c),add(b,a,c);}dfs1(1);dfs2(1,1);int q;cin>>q;while(q--){cin>>m;for(int i=1;i<=m;i++){cin>>is[i];mp[is[i]]=1;}build();cout<<dfs3(1)<<'\n';for(int i=1;i<=m;i++) mp[is[i]]=0;}return 0;
}

要加油哦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/319031.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【.NET Core项目实战-统一认证平台】第四章 网关篇-数据库存储配置(2)

上篇文章我们介绍了如何扩展Ocelot网关&#xff0c;并实现数据库存储&#xff0c;然后测试了网关的路由功能&#xff0c;一切都是那么顺利&#xff0c;但是有一个问题未解决&#xff0c;就是如果网关配置信息发生变更时如何生效&#xff1f;以及我使用其他数据库存储如何快速实…

计算机提示找不到vcruntime140.dll,无法继续执行代码怎么办?如何修复

“找不到vcruntime140.dll&#xff0c;无法继续执行代码”。这个问题可能会让你感到困惑&#xff0c;不知道如何解决。那么&#xff0c;vcruntime140.dll是什么文件&#xff1f;它为什么会丢失&#xff1f;又该如何解决这个问题呢&#xff1f;本文将为你详细介绍vcruntime140.d…

codeforces1486 F. Pairs of Paths(倍增+树上数数)

F. Pairs of Paths syksykCCC题解 iamhpp题解 首先说明&#xff0c;下面图片来自第一篇博客&#xff0c;下面代码照抄第二篇博客 对没有啥是自己写的&#xff08;因为我太菜~~ 从上图可以看出两条链只有一个交点可能有两种情况 交点是两条链的LCA交点是一条链的LCA而不是另一…

学习Raft算法的笔记

Raft是一种为了管理日志复制的一致性算法。它提供了和Paxos算法相同的功能和性能&#xff0c;但是它的算法结构和Paxos不同&#xff0c;使得Raft算法更加容易理解并且更容易构建实际的系统。为了提升可理解性&#xff0c;Raft将一致性算法分解成几个关键的模块&#xff0c;例如…

.NET Core 必备安全措施

.NET Core大大简化了.NET应用程序的开发。它的自动配置和启动依赖大大减少了开始一个应用所需的代码和配置量&#xff0c;本文目的是介绍如何创建更安全的.NET Core应用程序。1.在生产中使用HTTPS传输层安全性&#xff08;TLS&#xff09;是HTTPS的官方名称&#xff0c;你可能听…

[翻译] C# 8.0 新特性

原文: Building C# 8.0[译注:原文主标题如此&#xff0c;但内容大部分为新特性介绍&#xff0c;所以意译标题为 "C# 8.0 新特性"]C# 的下一个主要版本是 8.0。我们已经为它工作了很长一段时间&#xff0c;即使我们构建并发布了次要版本 C# 7.1, 7.2 和 7.3&#xff0…

[蓝桥杯][2018年第九届真题]搭积木

[[蓝桥杯][2018年第九届真题]搭积木](https://www.dotcpp.com/oj/problem2292.html)题目&#xff1a; 小明对搭积木非常感兴趣。他的积木都是同样大小的正立方体。 在搭积木时&#xff0c;小明选取 m 块积木作为地基&#xff0c;将他们在桌子上一字排开&#xff0c;中间不留空…

使用C#把Tensorflow训练的.pb文件用在生产环境

训练了很久的Tf模型&#xff0c;终于要到生产环境中去考验一番了。今天花费了一些时间去研究tf的模型如何在生产环境中去使用。大概整理了这些方法。继续使用分步骤保存了的ckpt文件这个貌似脱离不了tensorflow框架&#xff0c;而且生成的ckpt文件比较大&#xff0c;发布到生产…

牛客题霸 转圈打印矩阵 C++题解/答案

牛客题霸 转圈打印矩阵 C题解/答案 题目描述 给定一个整型矩阵matrix&#xff0c;请按照顺时针转圈的方式打印它。 示例1 输入 复制 [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]] 返回值 复制 [1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10] 题解&#xff1a; 题意很明确…

【招聘(上海)】东方财富证券招聘.net开发

东方财富证券&#xff1a;东方财富网(深创&#xff1a;300059)旗牌照齐全证券公司&#xff0c;极具互联网基因的券商。.Net服务端开发工程师/.Net前端开发工程师[职位介绍]服务端&#xff1a;开户、交易及资讯系统的后端接口服务(Asp.Net WebApi)开发&#xff1b;服务端&#x…

EF Core 实现多租户

SAAS 和多租户SaaS(软件及服务)区别于其他应用程序的主要特征就是能够使客户在使用应用程序时按照使用量付费。他们不需要为软件购买许可&#xff0c;也不需要安装、托管和管理它。这方面的操作全部由提供 SaaS 软件的组织负责。多租户是实现 SaaS 的关键因素, 它可以让多个企业…

牛客题霸 反转链表 C++题解/答案

题目描述 输入一个链表&#xff0c;反转链表后&#xff0c;输出新链表的表头。 示例1 输入 复制 {1,2,3} 返回值 复制 {3,2,1} 题解&#xff1a; 如果用偷懒的方法&#xff0c;可以用vector来存链表内容&#xff0c;然后来个翻转&#xff08;vector自带&#xff09;即可 但是…

【.NET Core项目实战-统一认证平台】第五章 网关篇-自定义缓存Redis

上篇文章【.NET Core项目实战-统一认证平台】第四章 网关篇-数据库存储配置&#xff08;2&#xff09;我们介绍了2种网关配置信息更新的方法和扩展Mysql存储&#xff0c;本篇我们将介绍如何使用Redis来实现网关的所有缓存功能&#xff0c;用到的文档及源码将会在GitHub上开源&a…

广州.net俱乐部12月份ABP框架活动场地征集、志愿者征集、合作讲师\副讲师征集...

大家好&#xff0c;我在<被低估的.net(上) - 微软MonkeyFest 2018广州分享会活动回顾>一文中提到&#xff0c;我将在12月份搞一场ABP框架活动&#xff0c;现向大家征集活动场地、志愿者、合作讲师\副讲师。活动课程标题是&#xff1a;如何用ABP框架快速完成项目这是内容大…

CF1446F-Line Distance【计算几何,树状数组,二分】

正题 题目链接:https://www.luogu.com.cn/problem/CF1446F 题目大意 给出nnn个点&#xff0c;求所有点对构成的直线中与原点距离第kkk小的距离 2≤n≤105,1≤k≤n(n−1)22\leq n\leq 10^5,1\leq k\leq \frac{n(n-1)}{2}2≤n≤105,1≤k≤2n(n−1)​ 解题思路 二分还是挺显然的…

为什么是容器,Docker和Kubernetes?

如果你是一名IT行业的从业者&#xff0c;还没有听说过以上3个词的任何一个&#xff0c;抱歉&#xff0c;你可以改行了&#xff1b;如果你是一名技术人员&#xff0c;无论你是程序员&#xff0c;测试人员&#xff0c;运维工程师还是时髦的DevOps工程师&#xff0c;你还没有运行过…

一份来自28岁.NET老程序员的自白

写在前面很幸运&#xff0c;28岁的我头发还没有掉光&#xff0c;更幸运的是28岁的我开始了博客园的写作生活&#xff01;这样的技术分享经历让我拓展了自己的朋友圈&#xff01;有幸结识了像张善友张队&#xff08;连续13年的微软MVP&#xff09;&#xff0c;大石头(NewLife团队…

谈谈.NET Core中基于Generic Host来实现后台任务

前言很多时候&#xff0c;后台任务对我们来说是一个利器&#xff0c;帮我们在后面处理了成千上万的事情。在.NET Framework时代&#xff0c;我们可能比较多的就是一个项目&#xff0c;会有一到多个对应的Windows服务&#xff0c;这些Windows服务就可以当作是我们所说的后台任务…

2018 KubeCon + CloudNativeCon完美落幕,行云献力

2018年11月13-15日&#xff0c;由云原生计算基金会&#xff08;CNCF&#xff09;组织的KubeConCloudNativeCon首次登陆中国。经过了三天的技术交流和展示&#xff0c;大会随着15日下午最后一个议程的结束而完美落幕。作为云原生领域全球最大的峰会&#xff0c;KubeConCloudNati…

魔方Newlife.Cube权限系统的使用及模版覆盖详解

讲人&#xff1a;大石头时间&#xff1a;2018-11-14 晚上20&#xff1a;00地点&#xff1a;钉钉群&#xff08;组织代码BKMV7685&#xff09;QQ群&#xff1a;1600800内容&#xff1a;魔方Newlife.Cube权限系统的使用及模版覆盖详解准备源码地址: https://github.com/NewLifeX/…