题意简述
给一个树(带权值,给定方法:第iii行两个数v,wv,wv,w,分别表示iii接到哪个点和边长),请依次输出从i(1<=i<=n)i(1<=i<=n)i(1<=i<=n)点开始能到的最长路长度。
数据
输入:
5
1 1//这是第2行,所以表示2和1之间有一个长度为1的边
2 1//同理,这个表示3—2[边长=1]
3 1//这个表示4–3[边长=1]
1 1//这个表示5–1[边长=1]
5
1 1
2 1
3 1
1 1
输出
3
2
3
4
4
思路
每个点开始暴力DFSDFSDFS是O(n)O(n)O(n)的(以每个点为根,记录深度,求最大值,O(n)O(n)O(n)处理),然后每次都要换点,就是O(n2)O(n^2)O(n2)了。
那么,我们想几个O(n)O(n)O(n)的情况、
1.O(1)O(1)O(1)转换,使得我们在枚举iii点的时候不用每次都新算,而是珂以继承,就O(n)O(n)O(n)了。
似乎不珂行。。。
2.O(n)O(n)O(n)求出所有答案,也就是把所有的点一块跑DPDPDP。这样总共也是O(n)O(n)O(n)的。
这个仿佛珂以,但如果不尝试的话,就真的没有办法了。
如果要这样搞,首先必须要有一个根,设1为根。
根据套路, 设dp[i]dp[i]dp[i]表示以iii为根能走到的最大长度。显而易见地dp[i]=max{dp[son]+dis(son,i)}dp[i]=max\{dp[son]+dis(son,i)\}dp[i]=max{dp[son]+dis(son,i)},也就是max{所有儿子的max\{所有儿子的max{所有儿子的dp值加上到儿子的距离}值加上到儿子的距离\}值加上到儿子的距离}。这实在是很好理解,不细讲。
但此时我们会发现,这样还不够。又不是所有点都一定只从下面经过就是最长路。看下面这个图就知道了。

如果这个时候,我们以红色点为起点,然后还只是考虑这个点以下的答案(即绿色部分),就不正确了。我们应该还要考虑蓝色的部分,也就是从iii点上面经过的最长路径。设这个值为dp[i][1]dp[i][1]dp[i][1],刚刚的是dp[i][0]dp[i][0]dp[i][0]。不难发现dp[i][1]dp[i][1]dp[i][1]就等于(i到父亲的距离+父亲到一个不经过i点的叶节点最长距离)。
我们知道边界条件dp[1][1]=0dp[1][1]=0dp[1][1]=0,因为111是根节点。
那么,如何转移呢?
关键就在于第二部分,也就是父亲到一个不经过iii点的最长距离。大概想想,好像只要找一个离iii父亲最远的叶节点走过去就是最远了(1),或者是直接继承iii父亲往上走的最大值(2)(这个要是有肯定毫不犹豫就继承了,过会会讲如何实现的)。(2)情况也就是dp[father][1]dp[father][1]dp[father][1],这个没什么难度。
但(1)情况不大全面,要分类一下。如果iii的父亲往下走的最长路要经过iii,就不能直接找最长了(不然走重了),珂怜的iii就只能继承iii父亲的第二长路了。
否则的情况iii就珂以继承iii父亲的第一长路。到此,我们就对这个dp[i][1]dp[i][1]dp[i][1]的转移差不多明白个大概了。但由于这个的思维难度实在是很大,所以看不懂也没有关系,要有耐心。
讲几个代码实现中要注意的问题:
1.由于dp[i][1]dp[i][1]dp[i][1]直接由父亲转移我不会写,所以我写的版本是由iii向iii的儿子作转移的方式
2.转移dp[i][1]dp[i][1]dp[i][1]的时候,设max1,v1max1,v1max1,v1分别为最大长度,此时经过的儿子。max2,v2max2,v2max2,v2分别为第二大长度,此时经过的儿子。对于uuu点的儿子vvv,如果v==v1v==v1v==v1,说明uuu点的最长路要从vvv经过,所以此时只能选第二大,即dp[v][1]=max2+wdp[v][1]=max2+wdp[v][1]=max2+w,其中www表示uuu到vvv的距离。
否则(即v!=v1v!=v1v!=v1的情况),vvv就珂以选最长路了,此时dp[v]=max1+wdp[v]=max1+wdp[v]=max1+w。
还有一个是刚刚留下的FlagFlagFlag:如何记录dp[u][1]dp[u][1]dp[u][1]的值?
如果dp[u][1]>max1dp[u][1]>max1dp[u][1]>max1,说明dp[u][1]dp[u][1]dp[u][1]是比所有往下走的路都要优的解,那么我们将max2,v2max2,v2max2,v2设置为max1,v1max1,v1max1,v1(即一次滑动操作),令max1=dp[u][1]max1=dp[u][1]max1=dp[u][1],把v1v1v1设置为−1-1−1。那么我们在考虑儿子vvv的时候,无论如何也不会==v1==v1==v1,此时就一定珂以继承v1v1v1的值,也就是dp[u][1]dp[u][1]dp[u][1]。
否则,如果dp[u][1]==max1dp[u][1]==max1dp[u][1]==max1或者dp[u][1]>max2dp[u][1]>max2dp[u][1]>max2,说明dp[u][1]dp[u][1]dp[u][1]虽然不能作为最优解,但是珂以作为第二优的解。用和上面类似的方法更新max2,v2max2,v2max2,v2的值,当v==v1v==v1v==v1的时候,就珂以来继承这个答案了。
好了,来看看代码吧:
#include<bits/stdc++.h>
#define N 101000
#define LogN 21
#define int long long
using namespace std;
class Graph//奇怪的存图
{
private:
int head[N];
int EdgeCount;
public:
struct Edge
{
int To,Label,Next;
}Ed[N<<1];
void clear()
{
memset(head,-1,sizeof(head));
memset(Ed,-1,sizeof(Ed));
EdgeCount=0;
}
void AddEdge(int u,int v,int w)//directed
{
EdgeCount++;
Ed[EdgeCount]=(Edge){v,w,head[u]};
head[u]=EdgeCount;
}
int Start(int u)
{
return head[u];
}
int Next(int u)
{
return Ed[u].Next;
}
int To(int u)
{
return Ed[u].To;
}
int Label(int u)
{
return Ed[u].Label;
}
}G;
int n;
void Add(int u,int v,int w)//加一个带权无向边
{
G.AddEdge(u,v,w);G.AddEdge(v,u,w);
}
void Input()
{
for(int i=2;i<=n;i++)
{
int v,w;
scanf("%lld%lld",&v,&w);
Add(i,v,w);
}
}
int dp[N][2];
//dp[i][0/1]:如上解释
void DFS1(int u,int fa)//求出所有dp[i][0]的值
{
dp[u][0]=0;
for(int i=G.Start(u);~i;i=G.Next(i))
{
int v=G.To(i);
if (v!=fa)
{
DFS1(v,u);
dp[u][0]=max(dp[u][0],dp[v][0]+G.Label(i));
}
}
}
void DFS2(int u,int f)//求出所有dp[i][1]的值
{
int max1=0,v1=-1;
int max2=0,v2=-1;//和上面一样
for(int i=G.Start(u);~i;i=G.Next(i))
{
int v=G.To(i),w=G.Label(i);
if (v!=f)
{
int tmp=dp[v][0]+w;//到叶节点的距离
if (tmp>max1)//说明这是最优解
{
max2=max1;v2=v1;//滑动一次
max1=tmp;v1=v;//更新最优
}
else if (tmp==max1 or tmp>max2)//说明这是次优解
{
max2=tmp;
v2=v;//更新次优
}
}
}
if (u!=1)//说明u不是根节点
{
int tmp=dp[u][1];//那么dp[u][1]就是有值的
if (tmp>max1)
{
max2=max1;v2=v1;
max1=tmp,v1=-1;
}
else if (tmp==max1 or tmp>max2)//这个上面讲了
{
max2=tmp;v2=-1;
}
}
for(int i=G.Start(u);~i;i=G.Next(i))//转移
{
int v=G.To(i),w=G.Label(i);
if (v!=f)
{
if (v==v1)
{
dp[v][1]=max2+w;
}
else
{
dp[v][1]=max1+w;
}
DFS2(v,u);
}
}
}
main()
{
while(~scanf("%lld",&n))//处理输入(相比上面的思维要简单的多吧。。。)
{
G.clear();
Input();
memset(dp,0,sizeof(dp));
DFS1(1,0);
DFS2(1,0);
for(int i=1;i<=n;i++) printf("%lld\n",max(dp[i][0],dp[i][1]));
}
return 0;
}
489

被折叠的 条评论
为什么被折叠?



