题意简述
给一个树(带权值,给定方法:第 i i i行两个数 v , w v,w v,w,分别表示 i i i接到哪个点和边长),请依次输出从 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
思路
每个点开始暴力
D
F
S
DFS
DFS是
O
(
n
)
O(n)
O(n)的(以每个点为根,记录深度,求最大值,
O
(
n
)
O(n)
O(n)处理),然后每次都要换点,就是
O
(
n
2
)
O(n^2)
O(n2)了。
那么,我们想几个
O
(
n
)
O(n)
O(n)的情况、
1.
O
(
1
)
O(1)
O(1)转换,使得我们在枚举
i
i
i点的时候不用每次都新算,而是珂以继承,就
O
(
n
)
O(n)
O(n)了。
似乎不珂行。。。
2.
O
(
n
)
O(n)
O(n)求出所有答案,也就是把所有的点一块跑
D
P
DP
DP。这样总共也是
O
(
n
)
O(n)
O(n)的。
这个仿佛珂以,但如果不尝试的话,就真的没有办法了。
如果要这样搞,首先必须要有一个根,设1为根。
根据套路, 设
d
p
[
i
]
dp[i]
dp[i]表示以
i
i
i为根能走到的最大长度。显而易见地
d
p
[
i
]
=
m
a
x
{
d
p
[
s
o
n
]
+
d
i
s
(
s
o
n
,
i
)
}
dp[i]=max\{dp[son]+dis(son,i)\}
dp[i]=max{dp[son]+dis(son,i)},也就是
m
a
x
{
所
有
儿
子
的
max\{所有儿子的
max{所有儿子的dp
值
加
上
到
儿
子
的
距
离
}
值加上到儿子的距离\}
值加上到儿子的距离}。这实在是很好理解,不细讲。
但此时我们会发现,这样还不够。又不是所有点都一定只从下面经过就是最长路。看下面这个图就知道了。
如果这个时候,我们以红色点为起点,然后还只是考虑这个点以下的答案(即绿色部分),就不正确了。我们应该还要考虑蓝色的部分,也就是从
i
i
i点上面经过的最长路径。设这个值为
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1],刚刚的是
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0]。不难发现
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1]就等于(i到父亲的距离+父亲到一个不经过i点的叶节点最长距离)。
我们知道边界条件
d
p
[
1
]
[
1
]
=
0
dp[1][1]=0
dp[1][1]=0,因为
1
1
1是根节点。
那么,如何转移呢?
关键就在于第二部分,也就是父亲到一个不经过 i i i点的最长距离。大概想想,好像只要找一个离 i i i父亲最远的叶节点走过去就是最远了(1),或者是直接继承 i i i父亲往上走的最大值(2)(这个要是有肯定毫不犹豫就继承了,过会会讲如何实现的)。(2)情况也就是 d p [ f a t h e r ] [ 1 ] dp[father][1] dp[father][1],这个没什么难度。
但(1)情况不大全面,要分类一下。如果 i i i的父亲往下走的最长路要经过 i i i,就不能直接找最长了(不然走重了),珂怜的 i i i就只能继承 i i i父亲的第二长路了。
否则的情况 i i i就珂以继承 i i i父亲的第一长路。到此,我们就对这个 d p [ i ] [ 1 ] dp[i][1] dp[i][1]的转移差不多明白个大概了。但由于这个的思维难度实在是很大,所以看不懂也没有关系,要有耐心。
讲几个代码实现中要注意的问题:
1.由于 d p [ i ] [ 1 ] dp[i][1] dp[i][1]直接由父亲转移我不会写,所以我写的版本是由 i i i向 i i i的儿子作转移的方式
2.转移 d p [ i ] [ 1 ] dp[i][1] dp[i][1]的时候,设 m a x 1 , v 1 max1,v1 max1,v1分别为最大长度,此时经过的儿子。 m a x 2 , v 2 max2,v2 max2,v2分别为第二大长度,此时经过的儿子。对于 u u u点的儿子 v v v,如果 v = = v 1 v==v1 v==v1,说明 u u u点的最长路要从 v v v经过,所以此时只能选第二大,即 d p [ v ] [ 1 ] = m a x 2 + w dp[v][1]=max2+w dp[v][1]=max2+w,其中 w w w表示 u u u到 v v v的距离。
否则(即 v ! = v 1 v!=v1 v!=v1的情况), v v v就珂以选最长路了,此时 d p [ v ] = m a x 1 + w dp[v]=max1+w dp[v]=max1+w。
还有一个是刚刚留下的 F l a g Flag Flag:如何记录 d p [ u ] [ 1 ] dp[u][1] dp[u][1]的值?
如果 d p [ u ] [ 1 ] > m a x 1 dp[u][1]>max1 dp[u][1]>max1,说明 d p [ u ] [ 1 ] dp[u][1] dp[u][1]是比所有往下走的路都要优的解,那么我们将 m a x 2 , v 2 max2,v2 max2,v2设置为 m a x 1 , v 1 max1,v1 max1,v1(即一次滑动操作),令 m a x 1 = d p [ u ] [ 1 ] max1=dp[u][1] max1=dp[u][1],把 v 1 v1 v1设置为 − 1 -1 −1。那么我们在考虑儿子 v v v的时候,无论如何也不会 = = v 1 ==v1 ==v1,此时就一定珂以继承 v 1 v1 v1的值,也就是 d p [ u ] [ 1 ] dp[u][1] dp[u][1]。
否则,如果 d p [ u ] [ 1 ] = = m a x 1 dp[u][1]==max1 dp[u][1]==max1或者 d p [ u ] [ 1 ] > m a x 2 dp[u][1]>max2 dp[u][1]>max2,说明 d p [ u ] [ 1 ] dp[u][1] dp[u][1]虽然不能作为最优解,但是珂以作为第二优的解。用和上面类似的方法更新 m a x 2 , v 2 max2,v2 max2,v2的值,当 v = = v 1 v==v1 v==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;
}