[树形dp]ICPC Nanjing 2021 H, Crystalfly解题思路
解题思路
我们注意到这题有个巧妙的地方 \(t_i\leq 3\),我们思考一下这个条件有什么用。
这就限制了我们在一个结点的选择,我们不可能走完两个很大的结点。如果我们站在当前的结点,我们只有几种可能。
- 对于当前的若干儿子,我们只能选择其中的一个权值
- 但是如果我们有一个 \(t_i=3\) 我们可以选择两个权值,但是要放弃另一个已经走过了的结点的两个儿子的权值。
我们好像发现每个点的权值是跟儿子有关的。
我们定义 \(f_i\) 表示我儿子是可以正常选但是不包括自己的最大贡献,\(g_i\) 表示我儿子都选不了了也不包括自己的最大贡献,我们考虑到对于现在这个点的 \(f,g\) 分别转移。
\(f_u=\max\{f_{v1}+g_{v2}+a_{v1}+a_{v2}+\sum_{v\not=v1\&v\not=v2}f_v\},t_{v1}=3\)
我们尝试合并一些东西
\(f_u=\max\{a_{v1}+g_{v2}+a_{v2}+\sum_{v\not=v2}f_v\},t_{v1}=3\)
我们好像看出什么了,我们会选一个 \(t_{v}=3\) 中 \(a\) 的最大值选择。那么 \(v2\) 是如何选择的呢。观察到如果一个点 \(v\) 被选择成了 \(v2\)。本来能有的贡献为 \(f_v\) 现在被迫之后的贡献为 \(g_v+a_v\)。中间的差值为 \(g_v+a_v-f_v\) 选择其中一个最大的就好了,但是我们要记住 \(v1\not= v2\)。但是我们不一定会优先选择 \(v1\) 也有可能优先选择 \(v2\) 我们两种情况都要考虑一下,这是一开始想到的,WA 了一发验证出来的。
或者我们可以不选择折返,这种情况下显得简单多了。
\(f_u=\max\{a_v+\sum f_v\}\)
显然那个单独的 \(v\) 我们选择 \(a_v\) 最大的即可。
我们如何转移 \(g\) 呢?如果是 \(g\),我们就不能正常的选择儿子了,那么没什么好纠结的。
\(g_u=\sum f_v\)
尝试一下把。
竟然自己 A 了这道题。
#include <bits/stdc++.h>
using namespace std;
template <typename T>inline void read(T& t){t=0; register char ch=getchar(); register int fflag=1;while(!('0'<=ch&&ch<='9')) {if(ch=='-') fflag=-1;ch=getchar();}while(('0'<=ch&&ch<='9')){t=t*10+ch-'0'; ch=getchar();} t*=fflag;}
template <typename T,typename... Args> inline void read(T& t, Args&... args) {read(t);read(args...);}
const int N=1e5+10, inf=0x3f3f3f3f;
typedef long long ll;
int T,n,t[N];
ll a[N],f[N],g[N];
vector<int>G[N];
void dfs(int u,int fa){
f[u]=g[u]=0;
for(int v:G[u]){
if(v==fa) continue;
dfs(v,u);
g[u]+=f[v];
}
// 不选择折返
ll maxx=0;
for(int v:G[u]) if(v!=fa) maxx=max(maxx,a[v]);
f[u]=maxx+g[u];
// 选择折返
maxx=0;
int maxh;
for(int v:G[u]) if(v!=fa&&t[v]==3&&a[v]>maxx) maxx=a[v],maxh=v;
ll minn=-inf*inf;
for(int v:G[u]) if(v!=fa&&v!=maxh&&g[v]+a[v]-f[v]>minn) minn=g[v]+a[v]-f[v];
f[u]=max(f[u],g[u]+maxx+minn);
maxh=0; minn=-inf*inf;
for(int v:G[u]) if(v!=fa&&g[v]+a[v]-f[v]>minn) minn=g[v]+a[v]-f[v],maxh=v;
maxx=0;
for(int v:G[u]) if(v!=fa&&v!=maxh&&t[v]==3&&a[v]>maxx) maxx=a[v];
f[u]=max(f[u],g[u]+maxx+minn);
}
int main(){
read(T);
while(T--){
read(n);
for(int i=1;i<=n;++i) G[i].clear();
for(int i=1;i<=n;++i) read(a[i]);
for(int i=1;i<=n;++i) read(t[i]);
for(int i=1;i<n;++i){
int u,v;
read(u,v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,-1);
printf("%lld\n",f[1]+a[1]);
}
return 0;
}
/*
是谁挥霍的时光啊,是谁苦苦的奢望啊
这不是一个问题,也不需要你的回答
No answer.
*/