20190105 东非大裂谷

本文深入探讨了DP算法在特定问题中的应用与优化策略,通过分析链状结构数据的特性,提出了一种基于链值单调性的高效算法。文章首先介绍了基本的DP算法实现,随后揭示了链值单调性这一关键性质,并基于此性质提出了改进的DP算法,显著提高了算法效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:
题面
数据范围

题解:
文科生误点进来我不负责!上一篇也是!
太弱了太弱了迟早要烷
观察不到性质,只会骗分。QAQ
我们可以很容易写出50分算法:设 d p [ i ] dp[i] dp[i]表示以i为根的子树且i为其中一个小组的起点时的最大值。
很显然我们可以在整棵子树中枚举点v,维护从v往上走到达的点、 W m a x W_{max} Wmax W m i n W_{min} Wmin、u所有子树的dp值的和。
然后就可以dp了。

50分代码(想看正解的直接往下拉):

#include<cstdio>
#include<vector>
#include<algorithm>
#define maxn 100005
#define maxm 5005
#define LL long long
using namespace std;
int n,w[maxn],deg[maxn],minx[maxn],maxx[maxn],fa[maxn];
LL dp[maxn],sumch[maxn];
struct node { int v; node *nxt; } edge[maxn],*head[maxn],*ncnt;
vector<int> ch[maxm];
void addedge(int u,int v)
{
	ncnt++;
	ncnt->v=v,ncnt->nxt=head[u];
	head[u]=ncnt;
}
void solve1(int u)
{
	minx[u]=maxx[u]=w[u],fa[u]=u;
	for(node *p=head[u];p;p=p->nxt)
	{
		int v=p->v;
		solve1(v);
		for(int i=0,siz=ch[v].size();i<siz;i++) ch[u].push_back(ch[v][i]);
		ch[u].push_back(v); sumch[u]+=dp[v];
	}
	for(int i=0,siz=ch[u].size();i<siz;i++)
	{
		int v=ch[u][i];
		minx[v]=min(minx[v],w[u]),maxx[v]=max(maxx[v],w[u]);
		sumch[v]+=sumch[u]-dp[fa[v]],fa[v]=u;
		dp[u]=max(dp[u],sumch[v]+maxx[v]-minx[v]);
	}
}
int main()
{
	scanf("%d",&n); ncnt=&edge[0];
	for(int i=1;i<=n;i++) scanf("%d",&w[i]);
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		addedge(u,v); deg[u]++;
	}
	if(n<=5000) solve1(1);
	printf("%lld\n",dp[1]);
}

然而我说了这是50%算法。
满分算法需要get到一个性质:每一条链的值应该是单调的。
感性认识:如果在a到b之间不单调,那么b后面那些数就没有贡献,完全可以把a和b断掉。
然而我觉得就算我发现了这个性质我也想不到下面的东西
于是我们可以令 f [ u ] [ 0 / 1 ] f[u][0/1] f[u][0/1]表示以u为根的子树,它为顶点的链不减/不增时的最大值。然后 d p [ u ] = max ⁡ { f [ u ] [ 0 ] , f [ u ] [ 1 ] } dp[u]=\max\{f[u][0],f[u][1]\} dp[u]=max{f[u][0],f[u][1]}即可。
至于如何求f?见代码啊QWQ
w [ v ] &gt; = w [ u ] w[v]&gt;=w[u] w[v]>=w[u],那么就是这条链不减, f [ u ] [ 0 ] = max ⁡ { f [ u ] [ 0 ] , s u m − d p [ v ] + f [ v ] [ 0 ] + w [ v ] − w [ u ] } f[u][0]=\max\{f[u][0],sum-dp[v]+f[v][0]+w[v]-w[u]\} f[u][0]=max{f[u][0],sumdp[v]+f[v][0]+w[v]w[u]}。不用算整条链的原因是中间的f值已经减去w[v]。可参考求数列通项的累加法
反之同理。

代码:

#include<cstdio>
#include<vector>
#include<algorithm>
#define maxn 100005
#define maxm 5005
#define LL long long
using namespace std;
int n,w[maxn],deg[maxn],minx[maxn],maxx[maxn],pos[maxn],fa[maxn],dep[maxn];
LL dp[maxn],f[maxn][2],sumch[maxn];
struct node { int v; node *nxt; } edge[maxn],*head[maxn],*ncnt;
vector<int> ch[maxm];
void addedge(int u,int v)
{
	ncnt++;
	ncnt->v=v,ncnt->nxt=head[u];
	head[u]=ncnt;
}
void solve1(int u)
{
	for(node *p=head[u];p;p=p->nxt)
	{
		int v=p->v; solve1(v);
		sumch[u]+=dp[v];
	}
	f[u][0]=f[u][1]=sumch[u];
	for(node *p=head[u];p;p=p->nxt)
	{
		int v=p->v;
		if(w[v]>=w[u]) f[u][0]=max(f[v][0]+sumch[u]-dp[v]+w[v]-w[u],f[u][0]);
		if(w[v]<=w[u]) f[u][1]=max(f[v][1]+sumch[u]-dp[v]+w[u]-w[v],f[u][1]);
	}
	dp[u]=max(f[u][0],f[u][1]);
}
int main()
{
	scanf("%d",&n); ncnt=&edge[0];
	for(int i=1;i<=n;i++) { scanf("%d",&w[i]); pos[i]=i; }
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		addedge(u,v); deg[u]++,fa[v]=u;
	}
	solve1(1);
	printf("%lld\n",dp[1]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值