CodeForces-1485E-Move and Swap

本文讲解了如何使用树形动态规划解决一个关于树结构的问题,即在一个有向树中,红色和蓝色点从头节点出发,通过有限步移动,最大化节点间绝对值之和。通过贪心策略的修正和深度优先搜索预处理,详细介绍了状态转移方程和关键优化步骤。
摘要由CSDN通过智能技术生成

题目大意

img

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Phoenix_ZengHao

创作不易,能否打赏一瓶饮料?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值