“科林明伦杯”哈尔滨理工大学第十届程序设计竞赛 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; }