题目大意
n个节点的树,每个节点(2~n)有值a[i],一开始红色点和蓝色点都在头节点1,移动d步求最大的|a[i]-a[j]|之和。
每一步:
红色点只能移动到r的子节点
蓝色点可以移动到下一层的任意一点
每次可以选择交换红色点和蓝色点的位置(也可以不交换)
思路
一开始想着贪心:每次选择每层最大的和最小的,但是这样贪心显然不对,样例都过不了。
于是想树形DP:
假设红色点和蓝色点不可以交换,dp[i]表示红色点当前走到i点时最大的总和,fa[i]表示节点i的父亲节点。那么dp[i]=max(dp[i],dp[fa[i]]+|a[i]-a[j]|)(j就是蓝色点,即与i同层的节点),这里可以依靠预处理出同一层最大的a[i]的值和最小的a[i]的值。如果红色点和蓝色点可以交换,那么dp[i]=max(dp[i],dp[fa[j]]+abs(a[i]-a[j])),即dp[i]=max(dp[i],dp[fa[j]]+a[j]-a[i],dp[fa[j]]-a[j]+a[i]),这里可以预处理出dp[fa[j]]+a[j]和dp[fa[j]]-a[j].
首先用dfs求出深度,将同一深度的放到一个集合,然后通过对每一个深度进行预处理求处最小的a[i]和最大的a[i]以及最大的dp[fa[i]]+a[i]和dp[fa[i]]-a[i].
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=4e5+50;
typedef long long ll;
int T,n,k=1,head[maxn];ll a[maxn],dp[maxn];
struct Edge{
int to,next;
}edge[maxn*2];
void add(int u,int v){
edge[++k].to=v;edge[k].next=head[u];head[u]=k;
}
int deep[maxn],fa[maxn],Maxx_deep;vector<int>s[maxn];
void dfs(int u,int f){
deep[u]=deep[f]+1;fa[u]=f;s[deep[u]].push_back(u);
Maxx_deep=max(Maxx_deep,deep[u]);
for(int i=head[u];i;i=edge[i].next){
if(edge[i].to==f)continue;
dfs(edge[i].to,u);
}
}
int main(){
cin>>T;
while(T--){
k=1;Maxx_deep=0;
cin>>n;
memset(head,0,sizeof(int)*(2*n+10));
memset(dp,0,sizeof(ll)*(n+5));
for(int i=2;i<=n;i++){
int f;cin>>f;
add(f,i);add(i,f);
}
for(int i=2;i<=n;i++)cin>>a[i];
dfs(1,1);
ll ans=-1e18;
for(int i=2;i<=Maxx_deep;i++){
ll minn=1e18,maxx=-1e18,maxxe1=-1e18,maxxe2=-1e18;
for(int j=0;j<s[i].size();j++){
minn=min(minn,a[s[i][j]]);
maxx=max(maxx,a[s[i][j]]);
maxxe1=max(maxxe1,dp[fa[s[i][j]]]+a[s[i][j]]);
maxxe2=max(maxxe2,dp[fa[s[i][j]]]-a[s[i][j]]);
}
for(int j=0;j<s[i].size();j++){
dp[s[i][j]]=max(dp[s[i][j]],dp[fa[s[i][j]]]+max(abs(a[s[i][j]]-minn),abs(a[s[i][j]]-maxx)));
dp[s[i][j]]=max(dp[s[i][j]],maxxe1-a[s[i][j]]);
dp[s[i][j]]=max(dp[s[i][j]],maxxe2+a[s[i][j]]);
ans=max(ans,dp[s[i][j]]);
}
}
cout<<ans<<endl;
for(int i=1;i<=Maxx_deep;i++)s[i].clear();
}
return 0;
}