【XSY3331】东非大裂谷(结论,DP)

一般这种 “分段,求每段极值和的最大值” 的题都有两个结论:

  1. 一段的最大值和最小值一定是该段的两个端点。

    证明:如果不是的话:那么我们显然可以把最小值和最大值所在位置之间的部分提取出来作为一段,而其他的部分分离出去,这样得到的答案肯定不劣。

  2. 一段肯定是单调不增或单调不降的。

    证明:如果不是的话:假设该段为 \(a_1,a_2,\cdots,a_n\)。由结论1,我们可以假设 \(a_1\) 该段的最小值、\(a_n\) 为该段的最大值(\(a_1\) 为最大、\(a_n\) 为最小同理)。然后若此段不是单调不降的,那么我们找到某一个 \(a_{mid}>a_{mid+1}\) 的位置,然后把该段划分为新的两段 \(a_1,\cdots,a_{mid}\)\(a_{mid+1},\cdots,a_n\),这样得到的答案肯定是更优的。

那么这题相当于把序列上的问题转移到树上,那么直接设 \(dp_{u,0/1}\) 表示只考虑 \(u\) 子树内,其中 \(u\) 所在段是单调不增/单调不升的最优答案。

#include<bits/stdc++.h>

#define N 100010
#define ll long long

using namespace std;

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<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int n,w[N];
int cnt,head[N],nxt[N],to[N];
ll dp[N][2];//0 上升 1 下降 

void adde(int u,int v)
{
	to[++cnt]=v;
	nxt[cnt]=head[u];
	head[u]=cnt;
}

void dfs(int u)
{
	ll sum=0;
	for(int i=head[u];i;i=nxt[i])
	{
		int v=to[i];
		dfs(v);
		sum+=max(dp[v][0],dp[v][1]);
	}
	for(int i=head[u];i;i=nxt[i])
	{
		int v=to[i];
		if(w[v]>=w[u]) dp[u][0]=max(dp[u][0],sum-max(dp[v][0],dp[v][1])+w[v]-w[u]+dp[v][0]);
		if(w[v]<=w[u]) dp[u][1]=max(dp[u][1],sum-max(dp[v][0],dp[v][1])+w[u]-w[v]+dp[v][1]);
	}
	dp[u][0]=max(dp[u][0],sum);
	dp[u][1]=max(dp[u][1],sum);
}

int main()
{
	n=read();
	for(int i=1;i<=n;i++) w[i]=read();
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		adde(u,v);
	}
	dfs(1);
	printf("%lld\n",max(dp[1][0],dp[1][1]));
	return 0;
}
/*
7
5 5 3 6 2 3 3
1 6
5 3
1 5
6 2
2 4
6 7
*/
/*
4
2 3 4 1
1 2
2 3
3 4
*/
posted @ 2022-10-30 11:01  ez_lcw  阅读(30)  评论(0编辑  收藏  举报