P6419 [COCI2014-2015#1] Kamp
题目描述
一颗树 nn 个点,n−1n−1 条边,经过每条边都要花费一定的时间,任意两个点都是联通的。
有 KK 个人(分布在 KK 个不同的点)要集中到一个点举行聚会。
聚会结束后需要一辆车从举行聚会的这点出发,把这 KK 个人分别送回去。
请你回答,对于 i=1∼ni=1∼n ,如果在第 ii 个点举行聚会,司机最少需要多少时间把 KK 个人都送回家。
输入格式
第一行两个整数 n,Kn,K 。
接下来 n−1n−1 行,每行三个数 x,y,zx,y,z 表示 xx 到 yy 之间有一条需要花费 zz 时间的边。
接下来 KK 行,每行一个数,表示 KK 个人的分布。
输出格式
输出 nn 个数。
第 ii 行的数表示:如果在第 ii 个点举行聚会,司机需要的最少时间。
输入输出样例
7 2 1 2 4 1 3 1 2 5 1 2 4 2 4 7 3 4 6 2 3 7
11 15 10 13 16 15 10
5 2 2 5 1 2 4 1 1 2 2 1 3 2 4 5
5 3 7 2 2
感觉这题没有紫题的难度....
换根dp的特征感觉还是非常明显的,就是答案“可被移动“
具体点就是可以在logn或者是n的时间内通过计算来把一个节点的答案转化得到另一个节点的答案
这题就很明显,虽然要分类讨论,还有其他的挺多的限制和要求...主要是分类讨论很烦人。。
我简单说一下思路吧
第一遍dfs直接把从每个点出发经过它子树内所有终点并返回的路劲长度算出来
这个是最基础的,还有一些其他要算的我后面会说
然后第二遍dfs就需要算出来全局的答案了,就是从一个点出发,经过全图所有点并返回的路径长
如果有仔细一点,推过样例的同学就会发现,答案要求的不是这个,而是不返回的最短路径
就是车车把最后一个人送回家后,离开停下,到此为止它开的最短路径
这个路径和我们统计的路径有什么区别呢?
很明显,有一个容易证明的贪心,这个最短路径,就是我们上面算出来的路径减去这个点到离这个点最远的点路径长
(因为不用返回嘛)
那么,很明显,如果我们只是统计一个子树的根节点到这个子树内的终点的最短路径,这个在第一个dfs内就可以直接完成
问题就在于,怎么计算整棵树范围的,也就是全图的答案。
假如这个子树的根节点就是这棵树的根,那很明显这个最短路径就是我们要的最短路径
那我们在第一遍dfs的时候所用的根就是我们第二遍换根dp的起点
那我们现在要考虑的就是,已知一个节点作为跟的时候的答案,怎么把这个答案转移到其子节点上?也就是如何换根
要分类讨论了
首先我们以1号节点为根
f[u] 从点u出发把所有在u的子树里的人送回家并返回u的距离
ans[u]从点u出发把所有人出发并返回u的距离
dis[u] 从u出发,在u的子树内,距离u最远的那个人的家到u的距离
sdis[u] 从u出发,在u的子树内,距离u次远的那个人家到u的距离并且这个人的家和dis[u]不在同一个子树内(这个东西的作用后面会说到)
up[u] 不在u的子树内,距离u最远的那个人的家到u的距离
siz[u] u及u的子树内有多少个人的家
f[u],siz[u],dis[u],sdis[u]在第一遍dfs内就可以求出来
关键的是up[u]和ans[u]的计算
设fa为u的父亲节点,w为u和fa之间的路径长度
ans[u]的计算规则
1.当siz[u]==0时,ans[u]=ans[fa]+w*2
2.当siz[u]==k(就是所有的重点都在这个子树内)时,ans[u]=f[u]
3.其他的情况,ans[u]=fa[u]
up[u]的计算规则就更复杂一些
1.当siz[u]==0,up[u]=up[fa]+w
2.当siz[u]==k,up[u]=0
3.其他情况
有两种,一种是最长的就是up[fa]+w
一种是最长的要经过它的兄弟节点
第二种还有两种情况。。
如果dis[u]>up[u](就是上面说的第二种情况的一种)但是这个dis[u]实际上是经过了u的,那肯定是不能要的
但是它的兄弟节点的可能却没有被考虑到
所以对每一个节点就要记录一个最长路,和一个不和最长路相交的次长路,也就是dis[u]和sdis[u]
那这样最后的答案就是ans[i]-max(up[i],dis[i])
有一说一真的没有紫题难度。。不知道为什么是紫题
代码
#include<bits/stdc++.h> #define ll long long using namespace std; inline ll read() { char c=getchar();ll a=0,b=1; for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1; for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b; } struct edge { ll next,to,v; }e[1000501]; ll head[1000501],tot,n,k,vis[1000501],f[1000001],ans[1000001],sum[1000001]; ll len[1000501],slen[1000501],dis[1000501],sdis[1000501];//·Ö±ðÊÇÀëi×îÔ¶µÄµãºÍ´ÎÔ¶µÄµãµÄ¾àÀ룬ÒÔ¼°ÕâÌõÁ´µÄµÚÒ»¸öµãÔÚÄÄÀï ll up[1000501]; inline void add(ll i,ll j,ll k) { e[++tot].next=head[i]; e[tot].to=j; e[tot].v=k; head[i]=tot; } void dfsfirst(ll x,ll fa) { if(vis[x])sum[x]++; for(ll i=head[x];i!=0;i=e[i].next) { ll u=e[i].to; if(u==fa)continue; dfsfirst(u,x); if(sum[u]) { ll now=len[u]+e[i].v; f[x]+=f[u]+e[i].v*2; if(len[x]<now) sdis[x]=dis[x], dis[x]=u, slen[x]=len[x], len[x]=now; else if(slen[x]<now) sdis[x]=u, slen[x]=now; } sum[x]+=sum[u]; } } void dfsecond(ll x,ll fa)//¸üÐÂÈ«¾Ö´ð°¸ { for(ll i=head[x];i!=0;i=e[i].next) { ll u=e[i].to; if(u==fa)continue; if(sum[u]==0) { ans[u]=ans[x]+e[i].v*2; up[u]=max(up[x],len[x])+e[i].v; } else if(sum[u]==k) { ans[u]=f[u]; up[u]=0; } else { ans[u]=ans[x]; if(dis[x]==u)up[u]=max(up[x],slen[x])+e[i].v;//Ϊʲô´Î³¤¾Í²»»á¾¹ý,ÒòΪÎÒÉÏÃæÿÌõ¶¼Ö»È¡ÁËÒ»Ìõ×î´óÖµ°¡¡£¡£¡£ else up[u]=max(up[x],len[x])+e[i].v; } dfsecond(u,x); } } int main() { n=read();k=read(); for(ll i=1;i<n;i++) { ll a=read(),b=read(),c=read(); add(a,b,c);add(b,a,c); } for(ll i=1;i<=k;i++) { vis[read()]=1; } dfsfirst(1,0); ans[1]=f[1];up[1]=0; dfsecond(1,0); for(ll i=1;i<=n;i++) { cout<<ans[i]-max(up[i],len[i])<<endl; } return 0; }