时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
OJ: 牛客
题目描述
这里有一棵树,每个点和每条边都存在一个价值。对于树上点对的价值,包括点对的起点和终点以及路径上边权值之和,不包括路径上其他点值。
求这颗树上最大的点对价值为多少。点对至少需要两个点。
输入描述:
输入 t t t,代表有 t t t组样例。每组样例第一行输入 n n n,代表有 n n n个点。接下来有 n − 1 n-1 n−1行,第 i i i行有a[i]
和b[i]
,代表a[i]
节点与 i i i节点存在一条边,且边的值为b[i]
, 2 < = i < = n 2<=i<=n 2<=i<=n。接下来一行有 n n n个值c[j]
,代表每个节点 j j j的价值, 1 < = j < = n 1<=j<=n 1<=j<=n。
( t < = 10 , n > 1 , n < 1 e 6 , a [ i ] < i , − 500 < = b [ i ] < = 500 , − 500 < = c [ j ] < = 500 ) (t<=10,n>1,n<1e6,a[i]<i,-500<=b[i]<=500,-500<=c[j]<=500) (t<=10,n>1,n<1e6,a[i]<i,−500<=b[i]<=500,−500<=c[j]<=500)
输出描述:
输出最大的点对价值
输入
1
4
1 -2
1 2
1 3
2 -2 3 4
输出
12
题解
树形dp。
包含节点u
的最大权值的路径有2种:
- 以点
u
为端点的路径 - 不以
u
为端点的路径
对于第一种情况,最大值是(以子节点为端点的路径的最大值加上此端点到子节点的边的权值)的最大值。
不过需要特别注意连接时要避开计算节点的权值(计算过程中我只保存了远端端点的权值)。我们只需要计算路径上边的权值和即可,节点的权值只计算两端的端点的权值。
对于第二种情况,取(以子节点为端点的路径的最大值加上此端点到子节点的边的权值)的前两大,然后让他俩连起来。
然后回溯求解,最后答案就是根节点的两种情况下的权值最大值。
代码
//牛客明伦杯-树形dp-点对
#include <bits/stdc++.h>
#define m_p make_pair
#define p_i pair<int, int>
#define _for(i, a) for(register int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(register int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
#define scl(x) scanf("%lld", &x)
#define sc(x) scanf("%d", &x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int maxn = 1000005;
const int maxm = 1000005;
const int maxp = 30;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const double eps = 1e-8;
const double E = 2.718281828;
inline int read() {
int x(0), f(1); char ch(getchar());
while (ch<'0' || ch>'9') {
if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0'&&ch <= '9') {
x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
int n;
LL c[maxn];
LL sum[maxn];
vector<int> ps;
LL ans;
vector< pair< int, LL > > G[maxn];
LL dfs(int u, int fa) {
ps.push_back(u);
int pos = ps.size();
LL m = -INF, fm = -INF, sm = -INF; //计算前两大(以子节点为端点的路径的最大值加上此端点到子节点的边的权值) 只保存远端端点的权值,不保存路径上端点的权值,也不保存近端端点的权值。
_for(i, G[u].size()) {
int v = G[u][i].first;
if(v == fa) continue;
LL w = G[u][i].second, tem = -INF;
tem = dfs(v, u) + w; //延申以点`v`为端点的路径
if(tem > fm) sm = fm, fm = tem;
else if(tem > sm) sm = tem;
}
ans = max(ans, fm + c[u]); //第一种情况:以点`u`为端点的路径+端点u的权值
ans = max(ans, fm + sm); //第二种情况:不以`u`为端点的路径
return max(fm, c[u]); //返回以当前节点为端点的权值的最大值(只含有远端的端点的权值,不计算路径上的点的权值)
}
void init() {
_rep(i, 1, n) G[i].clear();
ans = -INF;
}
void sol() {
init();
_rep(i, 2, n) {
int a = read();
LL b = read();
G[i].push_back(m_p(a, b));
G[a].push_back(m_p(i, b));
}
_rep(i, 1, n) c[i] = read();
dfs(1, 1);
printf("%lld\n", ans);
}
int main() {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
#endif
int T;
while(cin>>T) {
_for(i, T) {
n = read();
sol();
}
}
return 0;
}