[51nod]A树的双直径
Posted lanrtabe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[51nod]A树的双直径相关的知识,希望对你有一定的参考价值。
题目链接:
树形(DP)+换根(DP)。
最直观的想法是枚举一条边断开,在两颗子树中求最大直径相乘然后取最大值。
不过这题可能出现负数,那么答案可能是正数( imes)正数或者负数( imes)负数。
其实只需要考虑正数的情况(负数把边全部取反即可)
那么设枚举边((x,y)),其中(x)为(y)的父亲
那么我们需要求出以(y)为根子树中的最长链和除去(x)子树外的最长链
对于(y)子树中的最长链(InMax[y]),我们可以先考虑求出一个点为一端向下的最长链,记为(RMax[y])
然后可以简单地写出转移方程:(RMax[x]=Max(RMax[x],RMax[y]+Value(x,y)))
接着考虑一条最长链可能由(y)向下的两条链组成,我们需要求出以(y)点为“最高点”的最长链。
那么设(RMax1[x],RMax2[x],RMax3[x])分别表示(y)向下第(1/2/3)长的链长,用(RMax[y]+Value(x,y))去更新,答案就是(RMax1[x]+RMax2[x])。
于是就有(InMax[x]=Max(Max(InMax[y]),RMax1[x]+RMax2[x]))
接着是除去(x)子树(不包括(x))外的最长链,记为(OutMax[y])
那么第一种是由(x)向下的两条链构成,注意这个不一定是(RMax1[x]+RMax2[x]),要除去经过(y)点的链(所以要维护(3)条最长链)
第二种是(x)向下和向上的链共同构成,那么设(URMax[x])表示(x)向上的最长链,更新方法和(RMax)类似
第三种是不经过(x),也就是(OutMax[x])
那么所有转移到此结束(不过(12)点蜜汁WA,把根换成(n)就过了,想到一个Hack数据忘了,问题不大)
时间复杂度 (O(n))(我的常数巨大)
代码:
#include <cstdio>
#include <cstring>
typedef long long ll;
#define Clear(a) (memset(a,0,sizeof a))
inline ll Max(const ll a,const ll b){return a>b?a:b;}
inline void Out(const ll x){if(x>9)Out(x/10);putchar(x%10^48);}
int n,Head[400005],Next[800005],To[800005],Val[800005],En;
int RMax1p[400005],RMax2p[400005],RMax3p[400005];
ll RMax1[400005],RMax2[400005],RMax3[400005];
ll InMax[400005],OutMax[400005],URMax[400005],Ans;
inline void Add(const int x,const int y,const int z)
{Next[++En]=Head[x],To[Head[x]=En]=y,Val[En]=z;}
inline void Update(const int x,const int p,const ll v)
{
if(v>RMax1[x])
{
RMax3[x]=RMax2[x],RMax3p[x]=RMax2p[x];
RMax2[x]=RMax1[x],RMax2p[x]=RMax1p[x];
RMax1[x]=v,RMax1p[x]=p;
}
else if(v>RMax2[x])
{
RMax3[x]=RMax2[x],RMax3p[x]=RMax2p[x];
RMax2[x]=v,RMax2p[x]=p;
}
else if(v>RMax3[x])
RMax3[x]=v,RMax3p[x]=p;
}
inline ll QRMax(const int x,const int Del)
{return Del==RMax1p[x]?RMax2[x]:RMax1[x];}
inline ll QRMSum(const int x,const int Del)
{
if(Del==RMax1p[x])return RMax2[x]+RMax3[x];
if(Del==RMax2p[x])return RMax1[x]+RMax3[x];
return RMax1[x]+RMax2[x];
}
void DP(const int x,const int Pre)
{
for(int i=Head[x],y;i;i=Next[i])
if((y=To[i])!=Pre)
{
DP(y,x),Update(x,y,Val[i]+RMax1[y]);
InMax[x]=Max(InMax[x],InMax[y]);
}
InMax[x]=Max(InMax[x],QRMSum(x,0));
}
void DPS(const int x,const int Pre)
{
Ans=Max(Ans,InMax[x]*OutMax[x]);
for(int i=Head[x],y;i;i=Next[i])
if((y=To[i])!=Pre)
{
URMax[y]=Max(Max(QRMax(x,y),URMax[x])+Val[i],0);
OutMax[y]=Max(Max(QRMSum(x,y),Max(QRMax(x,y)+URMax[x],OutMax[x])),0);
DPS(y,x);
}
}
inline void Solve(){DP(1,0),DPS(1,0);}
int main()
{
scanf("%d",&n);
for(int i=2,x,y,z;i<=n;++i)
{
scanf("%d%d%d",&x,&y,&z);
Add(x,y,z),Add(y,x,z);
}
Solve();
for(int i=1;i<=En;++i)Val[i]=-Val[i];
Clear(InMax),Clear(OutMax),Clear(URMax);
Clear(RMax1),Clear(RMax2),Clear(RMax3);
Clear(RMax1p),Clear(RMax2p),Clear(RMax3p);
Solve(),Out(Ans),putchar('
');
return 0;
}
以上是关于[51nod]A树的双直径的主要内容,如果未能解决你的问题,请参考以下文章
51Nod.1766.树上最远点对(树的直径 RMQ 线段树/ST表)
[51nod] 1766树上的最远点对 树的直径 树剖LCA+ST表静态查询