P6419 COCI2014-2015#1 Kamp
P6419 COCI2014-2015#1 Kamp
换根 \(dp\) 的 trick。
题面
钦定 \(k\) 个关键点,求每个点出发,访问完所有关键点的距离最小值。
思路
设 \(g_u\) 为从点 \(u\) 出发,访问完子树内所有关键点后,回到点 \(u\) 的距离最小值。
\(s_u\) 为点 \(u\) 子树内关键点个数,\(E(u,v)\) 为边权。
\]
自然我们需要换根 dp 求出每个点作为根的时候的 \(g_u\),设这个答案为 \(f_u\)。
\(f\) 为 \(u\) 的父亲。
考虑换根转移:
- 若 \(s_u=0\),则 \(f_u=g_f+E(f,u)\times 2\),画图显而易见。
- 若 \(k-s_u=0\),则 \(f_u=g_u\),都在 \(u\) 的子树内,也显而易见。
- 其他情况,则 \(f_u=f_f\),把 \(u\) 作为根,仔细观察一下我们更新的递推式,你会发现 \(f_u=f_f-(g_u-E(u,f))+(g_u+E(u,f))\),化简即可得。
由于车开到最后一个关键点可以不返回 \(u\) 点,而最小访问距离是可以有多重解法的,肯定存在一种方案使得最后访问最距离 \(u\) 最远的点,这样就可以剩下最大距离不用返回 \(u\) 点。
下文所述的最长链,次长连均为关键点作为链的一端的链。
这个貌似可以使用 dfn 序+线段树维护最长链,单次查询复杂度 \(O(\log n)\),但这里也可以利用换根 dp 求最长链。
下面是利用换根 dp 求最长链的思路。
设 \(u\) 子树内到 \(u\) 的最长链长度为 \(len_u\),来自 \(u\) 的儿子 \(id_u\)。
设 \(u\) 子树内到 \(u\) 的次长链长度为 \(slen_u\),要求与 \(len_u\) 来自的儿子不相同。
\(v\) 为 \(u\) 的儿子,有转移:
- 若 \(len_u<len_v+E(u,v)\),则 \(slen_u\gets len_u,len_u\gets len_v+E(u,v),id_u\gets v\)。
- 若不满足 1 且 \(slen_u<len_v+E(u,v)\),则 \(slen_u\gets len_v+E(u,v)\)。
变换 \(len_u\) 的意义为到 \(u\) 点的最长链,\(slen_u\) 的意义为和 \(len_u\) 来自不同的 \(u\) 所相接的边的次长链。
易得根的 \(len\) 与 \(slen\) 为原来的值。
设 \(f\) 为 \(u\) 的父亲,考虑从根开始的换根转移:
- 若 \(len_u<len_f+E(u,f)\) 且 \(id_f\neq u\),则 \(slen_u\gets len_u,len_u\gets len_f+E(u,f),id_u\gets f\)。
- 若 \(len_u<slen_f+E(u,f)\),则 \(len_u\gets slen_f+E(u,f),id_u\gets -1\),此时无论 \(id_u\) 取何值,都不影响下一步转移。
- 若 \(slen_u<len_f+E(u,f)\) 且 \(id_f\neq u\),则 \(slen_u\gets len_f+E(u,f)\)。
- 若 \(slen_u<slen_f+E(u,f)\),则 \(slen_u\gets slen_f+E(u,f)\)。
满足上述转移中最靠前的一条,便可以退出转移。
到这里我们变可以输出答案 \(f_i-len_i\)。
CODE
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=5e5+5;
struct Edge
{
int tot;
int head[maxn];
struct edgenode{int to,nxt,val;}edge[maxn*2];
inline void add(int x,int y,int z)
{
tot++;
edge[tot].to=y;
edge[tot].nxt=head[x];
edge[tot].val=z;
head[x]=tot;
}
}T;
int n,k;
int s[maxn],id[maxn];
ll dp[maxn],f[maxn],len[maxn],slen[maxn];
inline int Max(int a,int b)
{
if(a<b) return b;
else return a;
}
inline void dfs_fir(int u,int fa)
{
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(v==fa) continue;
dfs_fir(v,u);s[u]+=s[v];
}
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to,w=T.edge[i].val;
if(v==fa) continue;
if(!s[v]) continue;
dp[u]+=dp[v]+w*2;
if(len[v]+w>=len[u]) slen[u]=len[u],len[u]=len[v]+w,id[u]=v;
else if(len[v]+w>=slen[u]) slen[u]=len[v]+w;
}
}
inline void dfs_sec(int u,int fa)
{
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to,w=T.edge[i].val;
if(v==fa) continue;
if(!s[v]) f[v]=f[u]+w*2,len[v]=len[u]+w;
else if(k-s[v])
{
f[v]=f[u];
if(id[u]!=v&&len[v]<len[u]+w) slen[v]=len[v],len[v]=len[u]+w,id[v]=u;
else if(len[v]<slen[u]+w) slen[v]=len[v],len[v]=slen[u]+w,id[v]=-1;
else if(id[u]!=v&&slen[v]<len[u]+w) slen[v]=len[u]+w;
else if(slen[v]<slen[u]+w) slen[v]=slen[u]+w;
}
else f[v]=dp[v];
dfs_sec(v,u);
}
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
T.add(x,y,z),T.add(y,x,z);
}
for(int i=1;i<=k;i++)
{
int x;
scanf("%d",&x);
s[x]=1;
}
dfs_fir(1,0);
f[1]=dp[1];
dfs_sec(1,1);
for(int i=1;i<=n;i++) printf("%lld\n",f[i]-len[i]);
}
后记
这个 trick 相较于数据结构优势在于可以 \(O(1)\),劣势在于不可以在线。
P6419 COCI2014-2015#1 Kamp的更多相关文章
- COCI2014/2015 Contest#1 D MAFIJA【基环树最大独立点集】
T1725 天黑请闭眼 Online Judge:COCI2014/2015 Contest#1 D MAFIJA(原题) Label:基环树,断环+树形Dp,贪心+拓扑 题目描述 最近天黑请闭眼在 ...
- 2018-2-6考试(COCI2014/2015 Contest#5)
T1:FUNGHI(1s,32M,50pts)得分:50 题意:给你8个数组成一个环,要你求出其中连续的4个数,让它们的和最大 题解:暴力求出每一连续4个数之和,比较一下就好 标签:模拟 C++ Co ...
- C++算法代码——求数列[coci2014/2015 contest #1]
题目来自:http://218.5.5.242:9018/JudgeOnline/problem.php?id=1815 题目描述 Mirko在数学课上以一种有趣的方式操作数列,首先,他写下一个数列A ...
- [Bzoj3743][Coci2015] Kamp【换根Dp】
Online Judge:Bzoj3743 Label:换根Dp,维护最长/次长链 题目描述 一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的. 有K个人(分布在K个不同的 ...
- BZOJ3743 : [Coci2014]Kamp
d[x][0]表示x点向下走且回到x点的最少代价 d[x][1]表示x点向下走但不回到x点的最少代价 d[x][2]表示x点向下走的最长路 d[x][3]表示x点向下走的次长路 u[x][0]表示x点 ...
- bzoj 3743 [ Coci 2015 ] Kamp —— 树形DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3743 一开始想到了树形DP,处理一下子树中的最小值,向上的最小值,以及子树中的最长路和向上的 ...
- 洛谷 P6419 Kamp 题解
明天就SX AFO了交篇题解%一下 这题大概是我第一道有独立思考切掉的紫题 之前的都是各种抄借鉴题解 为什么写这题的题解呢?另一个重要的原因是这样的↓ 翻了翻已有题解中的几篇,下面几种情况屡见不鲜 样 ...
- 2015 西雅图微软总部MVP峰会记录
2015 西雅图微软总部MVP峰会记录 今年决定参加微软MVP全球峰会,在出发之前本人就已经写这篇博客,希望将本次会议原汁原味奉献给大家 因为这次是本人第一次写会议记录,写得不好的地方希望各位园友见谅 ...
- 使用Visual Studio 2015 开发ASP.NET MVC 5 项目部署到Mono/Jexus
最新的Mono 4.4已经支持运行asp.net mvc5项目,有的同学听了这句话就兴高采烈的拿起Visual Studio 2015创建了一个mvc 5的项目,然后部署到Mono上,浏览下发现一堆错 ...
- TFS 2015 敏捷开发实践 – 在Kanban上运行一个Sprint
前言:在 上一篇 TFS2015敏捷开发实践 中,我们给大家介绍了TFS2015中看板的基本使用和功能,这一篇中我们来看一个具体的场景,如何使用看板来运行一个sprint.Sprint是Scrum对迭 ...
随机推荐
- LaTeX cleveref 宏包用法
介绍 cleveref 宏包是 LaTeX 中用于增强交叉引用功能的一个强大工具.它的主要特点是能够自动地按照不同元素的类型(如章节.图表等)生成格式化的引用,同时还支持定制引用格式,提供了比 LaT ...
- 【YashanDB知识库】数据库审计shutdown immediate操作导致数据库异常退出
[问题分类]功能使用 [关键字]数据库审计,shutdown immediate [问题描述]审计shutdown immediate 操作,数据库作主从切换时会导致数据库异常退出. [问题原因分析] ...
- SNERT内部集训-WEB
SNERT内部集训-WEB Day1-2 文件上传 靶场搭建 docker安装,docker pull c0ny1/upload-labs,docker run -it -d -p 8080:80 a ...
- 云边协同的RTC如何助力即构全球实时互动业务实践
由 51 CTO 主办的"WOT 全球技术创新大会 2023·深圳站"于 11 月 24 日 - 25 日召开,即构科技后台技术总监肖潇以"边缘容器在全球音视频场景的 ...
- MoCov1: 无监督视觉表征学习的动量对比《Momentum Contrast for Unsupervised Visual Representation Learning》(MoCo、动量对比、动态字典、队列维护、对比损失InfoNCE)
现在是2024年6月11日,21:49,刚找好要看的论文,师兄推荐的. 先占个坑,明天看,我累了(我没脑子了). 现在是6月12日,15:49,干正事了(又被别人影响了情绪,这不好,希望你改掉,好的, ...
- nexus 部署与设置
安装nexus df -h 先查看目录磁盘空间,我安装的版本占用了四个G 空间,目录文件空间不够导致启动失败 上传 nexus 压缩包,并解压 查询 8081 端口号是否被占用 sudo netsta ...
- WPF下使用FreeRedis操作RedisStream实现简单的消息队列
Redis Stream简介 Redis Stream是随着5.0版本发布的一种新的Redis数据类型: 高效消费者组:允许多个消费者组从同一数据流的不同部分消费数据,每个消费者组都能独立地处理消息, ...
- CTF中特别小的EXE是怎么生成的
我们在打CTF时候,出题的爷爷们给出的exe都很小 就10k左右,有的甚至就5k,那时候我很郁闷啊.现在我也能了啊哈哈 不多bb按如下操作: 我们来看看正常的release生成的代码 #include ...
- Microsoft 发布 .NET 9 RC 2
距离最终版本还有一个月的时间,Microsoft 已经交付了 .NET 9 的第二个也是最后一个候选版本..NET 团队在公告帖子中写道[1],"当我们为 11 月的 .NET 9 正式发布 ...
- ES6箭头函数和普通函数的区别
书写形式不同,箭头函数的定义方式更加简单 : this指向不同 箭头函数使用父级作用域的this 普通函数执行 window 箭头函数不能使用 new 生成实例对象 : 普通函数的参数是argumen ...