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

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=+;
  4. typedef long long ll;
  5. const ll inf=(1LL*<<);
  6. int n,m;
  7. struct node{
  8. int nxt,to,val;
  9. }e[*N];
  10. int hd[N],cnt;
  11. void add(int x,int y,int z){
  12. e[++cnt].nxt=hd[x];
  13. e[cnt].val=z;
  14. e[cnt].to=y;
  15. hd[x]=cnt;
  16. }
  17. ll dis[N];
  18. bool exi[N];
  19. bool has[N];
  20. ll f[N],g[N];
  21. ll hf[N],hg[N];
  22. bool oh[N];
  23. ll ans[N];
  24. int ff[N];
  25. void dfs(int x,int fa,ll d){
  26. dis[x]=d;
  27. ll sumf=;
  28. bool fl=false;//fl记录是否是叶子
  29. bool bla=false;//bla记录是否有一个子树里有关键点(不包括自己)
  30. for(int i=hd[x];i;i=e[i].nxt){
  31. int y=e[i].to;
  32. if(y==fa) continue;
  33. fl=true;
  34. dfs(y,x,d+e[i].val);
  35. ff[y]=x;
  36. has[x]|=has[y];
  37. if(has[y]){
  38. bla=true;
  39. sumf+=f[y]+e[i].val*;
  40. }
  41. }
  42. if(!has[x]||!fl||!bla) {
  43. f[x]=g[x]=;return;
  44. }
  45. f[x]=sumf;
  46. g[x]=inf;
  47. for(int i=hd[x];i;i=e[i].nxt){
  48. int y=e[i].to;
  49. if(y==fa) continue;
  50. if(has[y]){
  51. ll now=sumf-f[y]+g[y]-e[i].val;
  52. g[x]=min(g[x],now);
  53. }
  54. }
  55. }
  56. void sol(int x,int fa){
  57. if(x!=){
  58. ll sumf=;
  59. ll valf=;
  60. oh[fa]=;//oh[fa]记得清0,因为可能这个fa会作为多个son的father
  61. if(exi[fa]) oh[fa]=true;
  62. for(int i=hd[fa];i;i=e[i].nxt){
  63. int y=e[i].to;
  64. if(y==x) {
  65. valf=e[i].val;
  66. continue;
  67. }
  68. else if(y==ff[fa]){
  69. if(oh[y]){
  70. oh[fa]=;
  71. sumf+=hf[y]+*e[i].val;
  72. }
  73. }
  74. else{
  75. if(has[y]){
  76. oh[fa]=;
  77. sumf+=f[y]+*e[i].val;
  78. }
  79. }
  80. }
  81.  
  82. hf[fa]=sumf;
  83. hg[fa]=inf;
  84. if(oh[fa]){
  85. bool son=false,bla=false;//son记录除了x是否有儿子。bla同上含义
  86. for(int i=hd[fa];i;i=e[i].nxt){
  87. int y=e[i].to;
  88. if(y==x) continue;
  89. son=true;
  90. if(y==ff[fa]){
  91. if(oh[y]){
  92. bla=true;
  93. ll now=sumf-hf[y]+hg[y]-e[i].val;
  94. hg[fa]=min(hg[fa],now);
  95. }
  96. }
  97. else if(has[y]){
  98. bla=true;
  99. ll now=sumf-f[y]+g[y]-e[i].val;
  100. hg[fa]=min(hg[fa],now);
  101. }
  102. }
  103. if(!son||!bla) hf[fa]=,hg[fa]=;
  104. }
  105. else{
  106. hf[fa]=;
  107. hg[fa]=;
  108. }
  109.  
  110. ll ansf=f[x],ansg=inf;//注意ansg=inf,当有子树至少存在一个关键点,ansg就可以得到正确答案
  111. if(oh[fa]) ansf+=sumf+*valf;
  112.  
  113. for(int i=hd[x];i;i=e[i].nxt){
  114. int y=e[i].to;
  115. if(y==fa){
  116. if(oh[fa]){
  117. ll now=ansf-sumf+hg[fa]-e[i].val;
  118. ansg=min(ansg,now);
  119. }
  120. }
  121. else{
  122. if(has[y]){
  123. ll now=ansf-f[y]+g[y]-e[i].val;
  124. ansg=min(ansg,now);
  125. }
  126. }
  127. }
  128.  
  129. ans[x]=ansg;
  130. }
  131. if(exi[x]&&m==){//全场只有一个关键点,特判,就是0了 ,否则由于ansg的锅,就成了inf
  132. ans[x]=;
  133. }
  134. for(int i=hd[x];i;i=e[i].nxt){
  135. int y=e[i].to;
  136. if(y==fa) continue;
  137. sol(y,x);
  138. }
  139.  
  140. }
  141. int main()
  142. {
  143. scanf("%d%d",&n,&m);int x,y,z;
  144. for(int i=;i<=n-;i++){
  145. scanf("%d%d%d",&x,&y,&z);
  146. add(x,y,z);add(y,x,z);
  147. }int t;
  148. for(int i=;i<=m;i++){
  149. scanf("%d",&t);
  150. exi[t]=has[t]=;
  151. }
  152. dfs(,,);
  153. ans[]=g[];
  154. sol(,);
  155. for(int i=;i<=n;i++){
  156. printf("%lld\n",ans[i]);
  157. }
  158. return ;
  159. }
 
代码丑,而且会被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. C++自学成长之路(第一篇)

    今天开始我将开启C++自学成长之路,今天是第一天,在以前就一直在网上查找关于c++的资料,想买一本好一点的,权威一点的资料书,通过努力查找,我选择了c++ primer,在网上这本书的好评如潮.更多的 ...

  2. 【WPF】两则动画效果

    原文:[WPF]两则动画效果 引言 利用WPF的动画可以轻而易举的实现各种各样的特效,如擦除,滑动进入等,先看两个效果图 第一个效果 这个动画其实利用了OpacityMask和LinearGradie ...

  3. Selenium-ActionChainsApi接口详解

    ActionChains 有时候我们在通过Selenium做UI自动化的时候,明明能够在DOM树内看到这个元素,但是我在通过driver click.sendkey的时候,就是点击不到或无法输入字符串 ...

  4. JS计算混合字符串长度

    用的是正则表达式 var str = ”坦克是tank的音译”; var len = str.match(/[^ -~]/g) == null ? str.length : str.length +  ...

  5. CSS布局的一些技巧

    max-width 通常使元素水平居中用的较多的方法为: #main { width: 600px; margin: 0 auto; } 但是,当浏览器窗口比元素的宽度还要窄时,浏览器会显示一个水平滚 ...

  6. flask_admin 笔记四 自定义视图

    定义自己的视图 对于您的要求非常具体的情况,您很难用内置的ModelView类来满足这些需求,Flask-Admin使您可以轻松地完全控制并将自己的视图添加到界面中. 1)独立视图 可以通过扩展Bas ...

  7. OpenCV调整彩色图像的饱和度和亮度

    问题 如何调整彩色图像的饱和度和亮度 解决思路 详细步骤: 将RGB图像值归一化到[0, 1] 然后使用函数cvtColor进行色彩空间的转换 接下来可以根据处理灰度图像对比度增强伽马变换或者线性变换 ...

  8. cloudflare 加https、加SSL(加CF处理)实操流程

    建站过程中,少不了SSL证书等cf添加操作,cf,即cloudflare的简写 首先,点击如图“Add site”,弹出输入框后,填写已在如阿里云.goDaddy.freedom等域名平台购买的域名: ...

  9. PAT甲题题解-1111. Online Map (30)-PAT甲级真题(模板题,两次Dijkstra,同时记下最短路径)

    题意:给了图,以及s和t,让你求s到t花费的最短路程.最短时间,以及输出对应的路径.   对于最短路程,如果路程一样,输出时间最少的. 对于最短时间,如果时间一样,输出节点数最少的.   如果最短路程 ...

  10. python发送邮件脚本ssl 465端口

    #coding:utf8 from smtplib import SMTP_SSL from email.header import Header from email.mime.text impor ...