[51nod]A树的双直径

题目链接:

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;
}

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

时间: 2024-10-14 22:47:06

[51nod]A树的双直径的相关文章

【BZOJ3302】[Shoi2005]树的双中心 DFS

[BZOJ3302][Shoi2005]树的双中心 Description Input 第一行为N,1<N<=50000,表示树的节点数目,树的节点从1到N编号.接下来N-1行,每行两个整数U,V,表示U与V之间有一条边.再接下N行,每行一个正整数,其中第i行的正整数表示编号为i的节点权值为W(I),树的深度<=100 Output 将最小的S(x,y)输出,结果保证不超过19^9 Sample Input 5 1 2 1 3 3 4 3 5 5 7 6 5 4 Sample Outpu

51nod 1462 树据结构 | 树链剖分 矩阵乘法

题目链接 51nod 1462 题目描述 给一颗以1为根的树. 每个点有两个权值:vi, ti,一开始全部是零. Q次操作: 读入o, u, d o = 1 对u到根上所有点的vi += d o = 2 对u到根上所有点的ti += vi * d 最后,输出每个点的ti值(n, Q <= 100000) 有50%的数据N,Q <= 10000 注:所有数64位整数不会爆. 题解 这道题好神奇啊--看讨论版里的 AntiLeaf 大神的矩阵乘法打标记才找到思路,然后又看到 ccz181078 的

P2726 [SHOI2005]树的双中心 题解

CSDN同步 原题链接 简要题意: 给定一棵树,\(d_{x,y}\) 为 \(x\) 与 \(y\) 距离(\(d_{x,x} = 0\)),选出两个点 \(x,y\),最小化: \[\sum_{u \in V} (w_u \times \min(dis_{x,u} , dis_{y,u})) \] 这种水的树形dp 黑题,没几个人做真是太可惜了 首先我们要明白这个式子是什么意思. \(\min (dis_{x,u} , dis_{y,u})\),就是在 \(x\) 和 \(y\) 中找到较近

51Nod 1405 树的距离之和 (dfs)

题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1405 中文题面不解释了,两次dfs,第一次自下向上,第二次自上向下. ans[i]表示i节点的答案,cnt[i]表示i节点为root的子树的节点个数,d[i]表示i节点为root的子树的答案. 1 //#pragma comment(linker, "/STACK:102400000, 102400000") 2 #include <algor

51Nod 1405 树的距离之和(dp)

1405 树的距离之和 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题  收藏  关注 给定一棵无根树,假设它有n个节点,节点编号从1到n, 求任意两点之间的距离(最短路径)之和. Input 第一行包含一个正整数n (n <= 100000),表示节点个数. 后面(n - 1)行,每行两个整数表示树的边. Output 每行一个整数,第i(i = 1,2,...n)行表示所有节点到第i个点的距离之和. Input示例 4 1 2 3 2 4 2 Output

BZOJ3302: [Shoi2005]树的双中心

n<=50000的树,深度<=100,有点权,选两个点x,y,使最小. dis取了min之后,整个树就会以某条边为分界线分成两半,一半归一个点管.如果是两棵完全独立的树的话,那肯定分别取这两棵树的带权重心.但割掉某条边再找两边重心,这种情况不一定是合法情况.例如: 上图中,虚线边被断开,两边的重心分别是星标节点.这不是一个合法方案,但它显然不如一个合法方案的答案优: 所以放心大胆地割就好了.注意到本题中树的深度h很小,所以割边后涉及的子树信息修改操作都可以暴力修改. 把树以某点为根,希望能预处

51nod 1673 树有几多愁

lyk有一棵树,它想给这棵树重标号. 重标号后,这棵树的所有叶子节点的值为它到根的路径上的编号最小的点的编号. 这棵树的烦恼值为所有叶子节点的值的乘积. lyk想让这棵树的烦恼值最大,你只需输出最大烦恼值对1e9+7取模后的值就可以了. 注意一开始1号节点为根,重标号后这个节点仍然为根. update:数据保证叶子节点个数<=20. 例如样例中,将1,2,3,4,5重标号为4,3,1,5,2,此时原来编号为4,5的两个叶子节点的值为3与1,这棵树的烦恼值为3.不存在其它更优解. Input 第一

hdu4612 无向图中任意添加一条边后使桥的数量最少 / 无向图缩点+求树的直径

题意如上,含有重边(重边的话,俩个点就可以构成了边双连通). 先缩点成树,在求数的直径,最远的连起来,剩下边(桥)的自然最少.这里学习了树的直径求法:第一次选任意起点U,进行bfs,到达最远的一个点v(level最深)该点必然是树的直径的一个端点,,再从该点出发,bfs,到最深的一点,该点深度就是直径.(证明:先假设u,是直径上一点,S,T是直径的端点,设v!=t,则有(V,U)+(U,S)>(T,U)+(U,S),矛盾,故t=v:若u不是直径上一点,设u到直径上的一点为x,同理易证. 最后 缩

hdu4612 无向图中随意加入一条边后使桥的数量最少 / 无向图缩点+求树的直径

题意如上,含有重边(重边的话,俩个点就能够构成了边双连通). 先缩点成树,在求数的直径,最远的连起来,剩下边(桥)的自然最少.这里学习了树的直径求法:第一次选随意起点U,进行bfs,到达最远的一个点v(level最深)该点必定是树的直径的一个端点,,再从该点出发,bfs,到最深的一点.该点深度就是直径. (证明:先如果u.是直径上一点,S,T是直径的端点.设v!=t,则有(V,U)+(U,S)>(T,U)+(U,S),矛盾,故t=v:若u不是直径上一点.设u到直径上的一点为x.同理易证. 最后