Online JudgeBzoj3743

Label:换根Dp,维护最长/次长链

题目描述

一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的。

有K个人(分布在K个不同的点)要集中到一个点举行聚会。

聚会结束后需要一辆车从举行聚会的这点出发,把这K个人分别送回去。

请你回答,对于i=1~n,如果在第i个点举行聚会,司机最少需要多少时间把K个人都送回家。

输入

第一行两个数,n,K。

接下来n-1行,每行三个数,x,y,z表示x到y之间有一条需要花费z时间的边。

接下来K行,每行一个数,表示K个人的分布。

输出

输出n个数,第i行的数表示:如果在第i个点举行聚会,司机需要的最少时间。

样例

Input

5 3
0
1
0
1 4
2 5
4 5
3 5

Output

2

题解

对于根节点做聚餐点的情况很容易得出。

\(ans[root]=2*∑^{K}_{i=1}dep[person[i]]-max(dep[person[1..K]])\)

也就是,所有住处的深度之和*2(要往返到其他住处)\(-\)住处的最大深度(可以选择最后去深度最大的那个点,这样它就不用了计两次了)。这样求根节点做聚餐点的情况只用dfs一遍,找一个住处的最大深度即可。


求其他非根节点做聚餐点的情况。

对于它子树内的情况和上面一个求法,但对于子树外很明显要用到换根Dp​。维护一个最长/次长链即可,具体细节见代码。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=500100;
inline int read(){
int x=0;char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x;
}
struct edge{
int to,nxt,d;
}e[2*N];
int head[N],Ecnt,n,m;
inline void link(int u,int v,int d){
e[++Ecnt].to=v,e[Ecnt].d=d,e[Ecnt].nxt=head[u];
head[u]=Ecnt;
}
int cnt[N],son[N],ma1[N],ma2[N],len[N];
/*
cnt[]:子树中住处的个数
ma1/2[]:最长/次长链长度(在第一遍dfs中只包含子树中的链,第二遍dfs中考虑了子树外的链)
son[]:i的最长链由i的哪个儿子son引上来(在第二遍dfs中,如果最长链发生改变,则将son赋值为0,表示在子树外)
len[]:住处的深度总和(第二遍dfs时算上子树外住处离自己的距离总和) */
bool mark[N];
void dfs(int x,int fa){ //第一遍,处理子树
if(mark[x])cnt[x]=1;
for(register int i=head[x];i;i=e[i].nxt){
int y=e[i].to;if(y==fa)continue;
dfs(y,x);
cnt[x]+=cnt[y],len[x]+=len[y];
if(!cnt[y])continue;
len[x]+=e[i].d;
int nowLink=ma1[y]+e[i].d;
if(nowLink>=ma1[x]){
ma2[x]=ma1[x],ma1[x]=nowLink,
son[x]=y;
}
else if(nowLink>=ma2[x])ma2[x]=nowLink;
}
}
void redfs(int x,int fa){//第二遍,换根。处理子树外
for(register int i=head[x];i;i=e[i].nxt){
int y=e[i].to;if(y==fa)continue;
//将以x为根的所有状态转移到以y为根
len[y]=len[x];
if(cnt[y]==0)len[y]+=e[i].d;//y离所有标记点远了
if(cnt[y]==m)len[y]-=e[i].d;//y离所有标记点近了
int nowLink=(y==son[x]?ma2[x]:ma1[x])+e[i].d;
if(cnt[y]!=m){//!!!注意
if(nowLink>=ma1[y]){
ma2[y]=ma1[y],ma1[y]=nowLink;
son[y]=0;//y此时的最长链在它的子树外
}
else if(nowLink>=ma2[y])ma2[y]=nowLink;
}
redfs(y,x);
}
}
signed main(){
n=read(),m=read();
for(register int i=1;i<n;i++){
int u=read(),v=read(),d=read();
link(u,v,d),link(v,u,d);
}
for(register int i=1;i<=m;i++)mark[read()]=1;
dfs(1,0);
redfs(1,0);
for(register int i=1;i<=n;i++)printf("%lld\n",2*len[i]-ma1[i]);
return 0;
}

update:

一开始下面这个操作没有判(\(cnt[y]!=m\))但在bzoj上也A了,后来做到了重题[COCI2014/2015 Contest#1 F]发现会被下面这个数据搞掉。

if(cnt[y]!=m){//!!!注意
if(nowLink>=ma1[y]){
ma2[y]=ma1[y],ma1[y]=nowLink;
son[y]=0;//y此时的最长链在它的子树外
}
else if(nowLink>=ma2[y])ma2[y]=nowLink;
}
/*
输入:
5 2
2 5 1
2 4 1
1 2 2
1 3 2
4
5
正确输出:
5
3
7
2
2
*/

[Bzoj3743][Coci2015] Kamp【换根Dp】的更多相关文章

  1. 2018.09.28 bzoj3743: [Coci2015]Kamp(树形dp)

    传送门 这是一道很有意思的题. 我们把所有的关键点都提出来,当成一棵有边权的虚树. 然后发现虚树上除最后不回到虚根的那条路径外外每条边都会被走两遍. 显然要让答案最优,不走的路径应该在虚树的直径上,于 ...

  2. [bzoj3743 Coci2015] Kamp(树形dp)

    传送门 Description 一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的. 有K个人(分布在K个不同的点)要集中到一个点举行聚会. 聚会结束后需要一辆车从举行聚会的 ...

  3. [BZOJ4379][POI2015]Modernizacja autostrady[树的直径+换根dp]

    题意 给定一棵 \(n\) 个节点的树,可以断掉一条边再连接任意两个点,询问新构成的树的直径的最小和最大值. \(n\leq 5\times 10^5\) . 分析 记断掉一条边之后两棵树的直径为 \ ...

  4. 2018.10.15 NOIP训练 水流成河(换根dp)

    传送门 换根dp入门题. 貌似李煜东的书上讲过? 不记得了. 先推出以1为根时的答案. 然后考虑向儿子转移. 我们记f[p]f[p]f[p]表示原树中以ppp为根的子树的答案. g[p]g[p]g[p ...

  5. 换根DP+树的直径【洛谷P3761】 [TJOI2017]城市

    P3761 [TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公 ...

  6. 小奇的仓库:换根dp

    一道很好的换根dp题.考场上现场yy十分愉快 给定树,求每个点的到其它所有点的距离异或上m之后的值,n=100000,m<=16 只能线性复杂度求解,m又小得奇怪.或者带一个log像kx一样打一 ...

  7. 国家集训队 Crash 的文明世界(第二类斯特林数+换根dp)

    题意 ​ 题目链接:https://www.luogu.org/problem/P4827 ​ 给定一棵 \(n\) 个节点的树和一个常数 \(k\) ,对于树上的每一个节点 \(i\) ,求出 \( ...

  8. Acesrc and Travel(2019年杭电多校第八场06+HDU6662+换根dp)

    题目链接 传送门 题意 两个绝顶聪明的人在树上玩博弈,规则是轮流选择下一个要到达的点,每达到一个点时,先手和后手分别获得\(a_i,b_i\)(到达这个点时两个人都会获得)的权值,已经经过的点无法再次 ...

  9. bzoj 3566: [SHOI2014]概率充电器 数学期望+换根dp

    题意:给定一颗树,树上每个点通电概率为 $q[i]$%,每条边通电的概率为 $p[i]$%,求期望充入电的点的个数. 期望在任何时候都具有线性性,所以可以分别求每个点通电的概率(这种情况下期望=概率 ...

随机推荐

  1. 在vue中使用高德地图vue-amap

    1.安装 vue-amap我安装指定版本0.5.10的版本 npm i --save vue-amap@0.5.10 2.main.js中的配置 key申请地址教程:https://lbs.amap. ...

  2. leetcode-157周赛-5216-统计元音字母序列的数目

    题目描述: 方法:倒推 class Solution(object): def countVowelPermutation(self, n): MOD = 10 ** 9 + 7 a=e=i=o=u= ...

  3. NPM一Node包管理和分发工具

    NPM 全称 Node Package Manager Node包管理和分发工具,可以把NPM理解为前端的Maven 我们通过npm可以很方便地下载js库,管理前端工程 最近版本的node.js已经集 ...

  4. Windows where

    WHERE [/R dir] [/Q] [/F] [/T] pattern... 描述:    显示符合搜索模式的文件位置.在默认情况下,搜索是在当前目录和 PATH    环境变量指定的路径中执行的 ...

  5. SpringCloud及其五大常用组件之Eureka和Zuul

    1.springcloud简介 SpringCloud是Spring旗下的项目之一,它是微服务架构的一种实现方式. 官网地址:http://projects.spring.io/spring-clou ...

  6. ArrayList 扩容

    处理容量是0, 第一次add的时候扩充到10 int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容50% 变成 1.5倍 第二 ...

  7. iOS开发之SceneKit框架--SCNNode.h

    1.SCNNode简介 SCNNode是场景图的结构元素,表示3D坐标空间中的位置和变换,您可以将模型,灯光,相机或其他可显示内容附加到该元素.也可以对其做动画. 2.相关API简介 初始化方法 // ...

  8. IntelliJ IDEA更换主题样式分享

    原文地址:https://blog.csdn.net/liu865033503/article/details/79481785 .自定义主题样式网址:http://www.riaway.com/in ...

  9. linux常用软连接使用ln -s

    [软连接]另外一种连接称之为符号连接(Symbolic Link),也叫软连接.软链接文件有类似于Windows的快捷方式.它实际上是一个特殊的文件.在符号连接中,文件实际上是一个文本文件,其中包含的 ...

  10. printk 函数消息是如何记录的

    printk 函数将消息写入一个   LOG_BUF_LEN 字节长的环形缓存, 长度值从 4 KB 到 1 MB, 由配置内核时选择. 这个函数接着唤醒任何在等待消息的进程, 就是说, 任何在系统 ...