Loading

“科林明伦杯”哈尔滨理工大学第十届程序设计竞赛 A.点对最大值(树的直径/树形DP)

坑比输入,比赛时没A掉可惜了

题目描述

这里有一棵树,每个点和每条边都存在一个价值。对于树上点对的价值,包括点对的起点和终点以及路径上边权值之和,不包括路径上其他点值。
求这颗树上最大的点对价值为多少。点对至少需要两个点。

 

输入描述:

输入t,代表有t组样例。每组样例第一行输入n,代表有n个点。接下来有n-1行,第i行有a[i]和b[i],代表a[i]节点与i节点存在一条边,且边的值为b[i],2<=i<=n。接下来一行有n个值c[j],代表每个节点j的价值,1<=j<=n。
(t<=10,n>1,n<1e6,a[i]<i,-500<=b[i]<=500,-500<=c[j]<=500)

输出描述:

输出最大的点对价值

 

首先注意这题输入比较坑,n那一行实际上是第一行,然后从第二行开始输入边…

注意这个题的点对价值的定义:起点终点加上路径上的边权总和。那么可以联想到树的直径,关键在于两个端点怎么处理。其实很简单,只需要把d数组初始化成对应的点权即可。这里我套了蓝书的板子,此处d[x]的含义为:从节点x出发走向x为根的子树,能够到达的最远价值处(包含路径端点的点权以及经过的边的边权之和,不包含x的点权)。对于这个题而言,假设当前处理到的点的度不为1,那么这个初始化了的d[x]肯定会被后续更新掉,假设度为1,联想到求树的直径时,dfs到了叶子时其实啥都没干,叶子节点的d值也没有被更新,那么这个题要加上端点,我就把它初始化成点权,问题完美解决。

#include <bits/stdc++.h>
#define N 1000006
using namespace std;
int head[N],ver[2*N],edge[2*N],Next[2*N],p[N],tot=0,d[N],n;//d[x]为不包含本点的从x出发能走的最远路径和 (包括最远点的端点值 不包括x 
bool v[N];
long long ans;
void add(int x,int y,int z)
{
    ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
}
void dp(int x)
{
    v[x]=1;
    int i;
    for(i=head[x];i;i=Next[i])
    {
        int y=ver[i];
        if(v[y])continue;
        dp(y);
        if(ans<(long long)d[x]+d[y]+edge[i])
        {
            ans=(long long)d[x]+d[y]+edge[i];
        }
        d[x]=max(d[x],d[y]+edge[i]);
    }
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        tot=0;
        ans=-1e18;
        memset(head,0,sizeof(head));
        memset(ver,0,sizeof(ver));
        memset(edge,0,sizeof(edge));
        memset(Next,0,sizeof(Next));
        memset(d,0,sizeof(d));
        memset(v,0,sizeof(v));
        int i;
        cin>>n;
        for(i=2;i<=n;i++)
        {
            int a,b;
            cin>>a>>b;
            add(i,a,b);
            add(a,i,b);
        }
        for(i=1;i<=n;i++) 
        {
            scanf("%d",&p[i]);
            d[i]=p[i];
        }
        dp(1);
        cout<<ans<<endl;
    }
    return 0;
}

 

posted @ 2020-05-31 21:02  脂环  阅读(170)  评论(0编辑  收藏  举报