Description

一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的。
有K个人(分布在K个不同的点)要集中到一个点举行聚会。
聚会结束后需要一辆车从举行聚会的这点出发,把这K个人分别送回去。
请你回答,对于i=1~n,如果在第i个点举行聚会,司机最少需要多少时间把K个人都送回家。
(注:一个车可以同时乘坐k个人)

HINT

【数据规模】
K <= N <= 500000
1 <= x,y <= N, 1 <= z <= 1000000

Solution

首先一定是一个二次扫描+换根
我的方法:
考虑到,进入一个子树,一定是把这个子树的人都送完再回到子树的根,或者不回到根,就结束了。
所以,套路地,令f[i]表示,以i为根的子树,把所有的关键点遍历到,再回到i的最小花费。
g[i]表示,以i为根的子树,把所有关键点遍历到,不回到i的最小花费。
以1号点为根,
直接树形dp一次可以求出。
注意,子树里没有关键点的f,g都是0
 
然后考虑换根
1号点的答案已经得到,计入ans[1]
换根的时候,fa就是x之前的根,现在x要变成根,
但是,fa的f,g可能进到了x的子树。
所以,设hf[i]为不经过当前根所在的子树,遍历其他关键点,回到i的最小时间。
hg[i]表示,........................,..........,不回到i的最小时间(省略重复内容)
oh[i]表示,除了当前根所在子树外,其他子树里(包括自己)有没有关键点。
每次,更新fa的hg,hf,oh
 
然后,用x的f,g和fa的hg,hf,oh确定ans[x]
细节很多,找std对拍了半天:
1.x的fa找子树的时候,可能找到fa的father,就要用hf[father[fa]]了。
2.第一遍dfs时,可能x是叶子,可能x是关键点,但是子树里没有,可能x及其子树都没有关键点,这些情况f,g都是0
3.换根的时候,考虑x的fa的hg,hf,也要像上面一样考虑。
 
但是,每次换根的时候,要遍历fa的所有儿子。
菊花图直接T的飞起~~~
(但是bzoj数据水)
 

Code

#include<bits/stdc++.h>
using namespace std;
const int N=+;
typedef long long ll;
const ll inf=(1LL*<<);
int n,m;
struct node{
int nxt,to,val;
}e[*N];
int hd[N],cnt;
void add(int x,int y,int z){
e[++cnt].nxt=hd[x];
e[cnt].val=z;
e[cnt].to=y;
hd[x]=cnt;
}
ll dis[N];
bool exi[N];
bool has[N];
ll f[N],g[N];
ll hf[N],hg[N];
bool oh[N];
ll ans[N];
int ff[N];
void dfs(int x,int fa,ll d){
dis[x]=d;
ll sumf=;
bool fl=false;//fl记录是否是叶子
bool bla=false;//bla记录是否有一个子树里有关键点(不包括自己)
for(int i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa) continue;
fl=true;
dfs(y,x,d+e[i].val);
ff[y]=x;
has[x]|=has[y];
if(has[y]){
bla=true;
sumf+=f[y]+e[i].val*;
}
}
if(!has[x]||!fl||!bla) {
f[x]=g[x]=;return;
}
f[x]=sumf;
g[x]=inf;
for(int i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa) continue;
if(has[y]){
ll now=sumf-f[y]+g[y]-e[i].val;
g[x]=min(g[x],now);
}
}
}
void sol(int x,int fa){
if(x!=){
ll sumf=;
ll valf=;
oh[fa]=;//oh[fa]记得清0,因为可能这个fa会作为多个son的father
if(exi[fa]) oh[fa]=true;
for(int i=hd[fa];i;i=e[i].nxt){
int y=e[i].to;
if(y==x) {
valf=e[i].val;
continue;
}
else if(y==ff[fa]){
if(oh[y]){
oh[fa]=;
sumf+=hf[y]+*e[i].val;
}
}
else{
if(has[y]){
oh[fa]=;
sumf+=f[y]+*e[i].val;
}
}
} hf[fa]=sumf;
hg[fa]=inf;
if(oh[fa]){
bool son=false,bla=false;//son记录除了x是否有儿子。bla同上含义
for(int i=hd[fa];i;i=e[i].nxt){
int y=e[i].to;
if(y==x) continue;
son=true;
if(y==ff[fa]){
if(oh[y]){
bla=true;
ll now=sumf-hf[y]+hg[y]-e[i].val;
hg[fa]=min(hg[fa],now);
}
}
else if(has[y]){
bla=true;
ll now=sumf-f[y]+g[y]-e[i].val;
hg[fa]=min(hg[fa],now);
}
}
if(!son||!bla) hf[fa]=,hg[fa]=;
}
else{
hf[fa]=;
hg[fa]=;
} ll ansf=f[x],ansg=inf;//注意ansg=inf,当有子树至少存在一个关键点,ansg就可以得到正确答案
if(oh[fa]) ansf+=sumf+*valf; for(int i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa){
if(oh[fa]){
ll now=ansf-sumf+hg[fa]-e[i].val;
ansg=min(ansg,now);
}
}
else{
if(has[y]){
ll now=ansf-f[y]+g[y]-e[i].val;
ansg=min(ansg,now);
}
}
} ans[x]=ansg;
}
if(exi[x]&&m==){//全场只有一个关键点,特判,就是0了 ,否则由于ansg的锅,就成了inf
ans[x]=;
}
for(int i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa) continue;
sol(y,x);
} }
int main()
{
scanf("%d%d",&n,&m);int x,y,z;
for(int i=;i<=n-;i++){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);add(y,x,z);
}int t;
for(int i=;i<=m;i++){
scanf("%d",&t);
exi[t]=has[t]=;
}
dfs(,,);
ans[]=g[];
sol(,);
for(int i=;i<=n;i++){
printf("%lld\n",ans[i]);
}
return ;
}
 
代码丑,而且会被hack掉。
 

正解:

以某个关键点为根,
对于所有的关键点建一棵虚树tree,所有的权值和是sum
虚树内的节点x的答案,就是sum*2再减去x往下最长链的长度。
虚树外的节点x的答案(一定是虚树某个节点的儿子),就是进入这个虚树的距离,加上进入点在虚树里的答案。
 
换根还是要考虑x的fa的最长链可能经过x。
所以,对于每个点记录一个最长链,一个次长链,(两个链从不同的儿子下去)和它们是从节点的哪一个儿子dp得到的。
rt从fa换到x,
如果fa的最长链不经过x,当前的最长链就是,fa的最长链加上x到fa的边权。
如果fa的最长链经过x,当前的最长链就是,x的最长链,和边权加上fa的次长链取个max
如果fa没有次长链,即fa只有x一个儿子,那么就是x的最长链和边权取个max
 
 
 
 

[Coci2015]Kamp的更多相关文章

  1. 【BZOJ3743】[Coci2015]Kamp 树形DP

    [BZOJ3743][Coci2015]Kamp Description 一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的. 有K个人(分布在K个不同的点)要集中到一个点举 ...

  2. bzoj3743 [Coci2015]Kamp 常州模拟赛d6t2

    3743: [Coci2015]Kamp Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 484  Solved: 229[Submit][Status ...

  3. bzoj3743: [Coci2015]Kamp

    首先树dp求出一个点的答案 然后再一遍dfs换根(是叫做换根吗.. 详见代码 #include <iostream> #include <cstdio> #include &l ...

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

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

  5. bzoj 3743: [Coci2015]Kamp【树形dp】

    两遍dfs一遍向下,一边向上,分别记录子树内人数a,当前点到所有点的距离b,最大值c和次大值d,最大值子树p 然后答案是2b-c #include<iostream> #include&l ...

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

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

  7. [Bzoj3743][Coci2015] Kamp【换根Dp】

    Online Judge:Bzoj3743 Label:换根Dp,维护最长/次长链 题目描述 一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的. 有K个人(分布在K个不同的 ...

  8. bzoj 3743 [Coci2015]Kamp——树形dp+换根

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3743 树形dp+换根. “从根出发又回到根” 减去 “mx ” . 注意dfsx里真的要改那 ...

  9. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

随机推荐

  1. 2017-2018-2 『网络对抗技术』Exp2:后门原理与实践

    1. 后门原理与实践实验说明及预备知识 一.实验说明 任务一:使用netcat获取主机操作Shell,cron启动 (0.5分) 任务二:使用socat获取主机操作Shell, 任务计划启动 (0.5 ...

  2. 20155330 《网络攻防》Exp1 PC平台逆向破解(5)M

    20155330 <网络攻防>Exp1 PC平台逆向破解(5)M 实践目标 运行pwn1可执行文件中的getshell函数,学习如何注入运行任何Shellcode 本次实践的对象是一个名为 ...

  3. Redis简介、安装、配置、启用学习笔记

    前一篇文章有介绍关系型数据库和非关系型数据库的差异,现在就来学习一下用的较广的非关系型数据库:Redis数据库 Redis 简介 Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-v ...

  4. 有关ADO.NET基础中的基础的熟悉过程

    现在对于ADO.NET基础的理解与记忆并不严谨和完善 所以,只写一点关于自己的理解,嗯,一种去转换思维理解的方法吧,算是吧 希望各位前辈或者同学,积极指出其中的错误和偏差 个人对于刚接触的ADO.NE ...

  5. JS基础内容小结(DOM&&BOM)(二)

    元素.childNodes:只读 属性 子节点列表集合 元素.nodeType:只读 属性 当前元素下的节点类型 元素.attributes : 只读 属性 属性列表集合 元素.children: 只 ...

  6. c++时间计算模块

    c++时间计算模块 可用于计算代码运行耗时.计算代码运行时间线(比如处理与运行时间相关函数). 该模块从实际项目中产生,使用方式仁者见仁智者见智,设计思想可供参考. 源码: //author: cai ...

  7. PAT甲题题解-1101. Quick Sort (25)-大水题

    快速排序有一个特点,就是在排序过程中,我们会从序列找一个pivot,它前面的都小于它,它后面的都大于它.题目给你n个数的序列,让你找出适合这个序列的pivot有多少个并且输出来. 大水题,正循环和倒着 ...

  8. 《linux内核分析》第六周:分析fork函数对应的系统调用处理过程

    一. 阅读理解task_struct数据结构http://codelab.shiyanlou.com/xref/linux-3.18.6/include/linux/sched.h#1235: 进程是 ...

  9. Eat Style --proposed by Chongyang Bai

    NEED 1. 有人希望妈妈是这样的: 但实际上对妈妈做的菜反应确是这样的: 处在不同的时节,根据不同的个人偏好,到底该做些什么饭菜?工作繁忙,家里的厨师可能也没时间琢磨.最后做出的只是应付差事的饭菜 ...

  10. Beta阶段敏捷冲刺前准备

    一.介绍小组新加入的成员,Ta担任的角色. 新成员一:徐璐琳 风格:酷酷哒 擅长的技术:JAVA,CCNP 编程的兴趣:折磨人的快感 新角色:管理员 一句话宣言:打开开关又是一个机会 新成员二:祁泽文 ...