码迷,mamicode.com
首页 > 其他好文 > 详细

[51nod]A树的双直径

时间:2019-03-17 23:00:39      阅读:171      评论:0      收藏:0      [点我收藏+]

标签:cpp   void   取反   can   cstring   str   val   scanf   .com   

题目链接:

51nod

树形\(DP\)+换根\(DP\)

最直观的想法是枚举一条边断开,在两颗子树中求最大直径相乘然后取最大值。

不过这题可能出现负数,那么答案可能是正数\(\times\)正数或者负数\(\times\)负数。

其实只需要考虑正数的情况(负数把边全部取反即可)

那么设枚举边\((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('\n');
    return 0;
}

[51nod]A树的双直径

标签:cpp   void   取反   can   cstring   str   val   scanf   .com   

原文地址:https://www.cnblogs.com/LanrTabe/p/10549303.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!