题意:
给一个树,并且每个节点都有一只蝴蝶,蝴蝶具有价值val。起初,你在1节点,你可以选择到下一个点去拿蝴蝶,获得它的价值,拿蝴蝶的时间不计。每个蝴蝶都有一个停留时间,当你在树上的某个节点时,该节点下的所有孩子结点处的蝴蝶的停留时间开始计时,当到达停留时间时,蝴蝶会飞走,该地方变成空的,此外,蝴蝶的停留时间满足 1 <= t <= 3,树的节点的数量1 <= n <= 1e6,从相连的一点走到另一点花费时间为1,可假设总共供捡蝴蝶的时间为无穷大;
输入:
给一个数n,代表节点的数量,接下来是n个值,代表每个蝴蝶的价值val,然后下一行的n个值代表的是每个节点处蝴蝶的停留时间t,接下来给n - 1条边,代表x与y之间有一条边。
输出:
输出最大可能捡到的val值总和。
思路:
首先,显然是个树上dp,对于绝大部分树上DP问题,首先要讨论选择儿子或者不选择儿子,以便对儿子的影响能够通过两个状态来算出来。
该题,f [ u ]为u节点能够获得的最大价值val,g [ u ] 节点为不选择 u 的所有孩子可以获得的最大价值val.
对于 g [ u ] 的计算比较简单,由于不选的子节点,所以对与孙子节点的选择没有影响,所以可得
g [ u ] = sum ( f [ v ] - val [ v ] );
接下来,对于 f [ u ] 的计算,首先要考虑,是否有些一层可以拿多个孩子,此时便可以考虑到
t [ v] = 3时,可以拿了兄弟再回来拿。
因此,接下来分情况考虑 :
对于t <= 2的蝴蝶,只要到了父亲节点,所有的兄弟节点只可以选择一个。
f [ u ] = max ( f [ u ], g[ u ] + val [ v ] );
对于 t == 3 的蝴蝶 v ,由于可以选了一个兄弟 br 之后再回来选择,所以此时
该兄弟下 br 的儿子不可以选择,该兄弟处的贡献最大值变为了 g [ br ],而自己处可以选择,可以得到选择某兄弟 br 可得最大贡献:
f [ u ] = max ( f [ u ], g [ u ] + val [ v ] + g [ br ] - ( f [ br ] - val [ br ] );
对于相减的原因是,首先g [ u ] 会带上 br 的孩子,而加上 br 后,也会带上 br 的孩子,所以 br 的孩子加了两次,为了减去这两次,可以考虑到br 处取得最大值,而不选上 br 的值,便是
f [ br ] - val [ br ];
我们可以发先,在遍历 u 的儿子时,记录 g [ v ] - f [ v ] + val [ v ] 的值,便可以 on 记录下最优选择的兄弟。
坑点:
memset会超时,不能memset;
cin其实也会超时(IOS 不会);
记录最优的 br 时,还应该考虑,这个 br 是不是自己,所以要记录最大值和次大值(相同没事);
代码如下:
// f , g , t 的含义如上
// G记录边
// p 就是val
#include <bits/stdc++.h>
#define pb push_back
#define int long long
using namespace std;
const int maxn = 1e6 +5;
const int inf = 0x3f3f3f3f;
int f[maxn],g[maxn];
vector <int> G[maxn];
int t[maxn],p[maxn],n;
void DP(int fa,int u)
{
// cout << x << endl;
g[u] += p[u];
int maxx = -inf,maxy = -inf;
for(auto v:G[u]) {
if(v == fa) continue;
DP(u,v);
g[u] += f[v] - p[v];
// cout << g[v] << " " << f[v] << " " << v << endl;
int now = g[v] - f[v] + p[v];
if(now > maxx) maxy = maxx,maxx = now;
else if(now > maxy) maxy = now;
}
f[u] = g[u];
for(auto v:G[u]) {
if(v == fa) continue;
f[u] = max(f[u],g[u] + p[v]);
if(t[v] == 3) {
if(g[v] - f[v] + p[v] == maxx) f[u] = max(g[u] + p[v] + maxy,f[u]);
else f[u] = max(g[u] + p[v] + maxx,f[u]);
}
}
}
signed main()
{
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T;cin >> T;
while(T--) {
cin >> n;
for(int i = 1;i <= n;i++) cin >> p[i],G[i].clear(),f[i] = g[i] = 0;
for(int i = 1;i <= n;i++) cin >> t[i];
for(int i = 1;i < n;i++) {
int x,y;cin >> x >> y;
G[x].pb(y);
G[y].pb(x);
}
DP(-1,1);
// for(int i = 1;i <= n;i++) {
// cout << f[i] << " ";
// }
// cout << endl;
cout << f[1] << endl;
}
return 0;
}
/*
*/