[51nod]A树的双直径

Posted lanrtabe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[51nod]A树的双直径相关的知识,希望对你有一定的参考价值。

题目链接:

51nod

树形(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表)

51 nod 1427 文明 (并查集 + 树的直径)

[51nod] 1766树上的最远点对 树的直径 树剖LCA+ST表静态查询

[51nod] 1766树上的最远点对 树的直径 树剖LCA+ST表静态查询

做题51Nod1766树上的最远点对——直径&线段树

51nod 1766 树上的最远点对(线段树)