「CF1580D」Subsequence 题解

本文网址:https://www.cnblogs.com/zsc985246/p/17509039.html ,转载请注明出处。

传送门

「CF1580D」Subsequence

题目大意

有一个长度为 n 的整数序列 a,所有元素都是不同的。

定义一个子序列 ab1,ab2,...,abm 的价值为

i=1m(mabi)i=1mj=1mf(min(bi,bj),max(bi,bj))

其中 f(i,j)=min(ai,ai+1,...,aj)

你需要选择一个长度为 ma 的子序列,使得其价值最大。

如果一个序列 s 可以通过删除序列 t 中几个元素(可以不删除任何元素或删除全部元素)得到,那么这个序列 s 就是序列 t 的一个子序列。

1mn4000,1ai<231

思路

价值的定义式非常难看,我们先将它化简。

发现当 i=j 时,f(min(bi,bj),max(bi,bj))=abi

所以我们直接提出来。

=i=1m(m1)abii=1mj=1,ijmf(min(bi,bj),max(bi,bj))

发现后面的式子 i=x,j=yi=y,j=x 的答案是相同的,所以只考虑 i<j

=i=1m(m1)abii=1mj=i+1m2×f(bi,bj)

然后发现,前面的求和每个数都算了 m1 次,而后面的求和是我们熟悉的,每个数也被算了 m1 次,所以可以把前面的求和拆进后面。

=i=1mj=i+1mabi+abj2×f(bi,bj)

后面的 abi+abj2×f(bi,bj) 感觉有点熟悉。我们求树上两点距离不就是 depx+depy2×deplca(x,y) 吗?

所以我们需要构造一棵树,使得一点 x 到根节点的距离为 ax,且两点 x,ylca 到根节点的距离为 f(x,y)

第一个条件很好满足,只需要让 x 连向 fax 的边权为 axafax 即可。

对于第二个条件,我们知道 f(x,y) 在原序列上表示区间 [x,y] 的最小值。这不就是笛卡尔树?

然后我们就只需要在树上找出 m 个点,使得这些点的两两距离之和最大。

我们直接做树上背包。

因为边的贡献只与两边选择的点的个数有关,所以可以只考虑子树内贡献最大。

定义 fx,i 表示在 x 子树中选择 i 个点,子树外选 mi 个点,子树内边权的贡献最大值。

转移只需要枚举 sonx 子树内的点数和 x 子树内 sonx 子树外的点数。复杂度 O(n2)

代码实现

#include<bits/stdc++.h>
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
const ll N=4000+10;
using namespace std;

ll n,m;
ll a[N];
ll top,s[N];
ll son[N][2],w[N][2];//0表示左儿子,1表示右儿子
ll siz[N];//大小
ll f[N][N];//f[x][i]表示在x子树中选择i个点,子树内边权的贡献最大值

void dfs(ll x){
	siz[x]=1;
	For(I,0,1){
		ll y=son[x][I];
		if(y){
			dfs(y);
			Rep(i,min(m,siz[x]),0){//x子树内y子树外的点数
				Rep(j,min(m,siz[y]),0){//y子树内的点数
					//子树内j个,子树外m-j个,两两配对就是经过次数
					f[x][i+j]=max(f[x][i+j],f[x][i]+f[y][j]+j*(m-j)*w[x][I]);
				}
			}
			siz[x]+=siz[y];
		}
	}
}

int main(){
	
	scanf("%lld%lld",&n,&m);
	For(i,1,n){
		scanf("%lld",&a[i]);
	}
	//建笛卡尔树
	For(i,1,n){
		ll t=top;
		while(top&&a[s[top]]>a[i])son[s[top-1]][1]=s[top],top--;
		if(top!=t)son[i][0]=s[top+1];
		s[++top]=i;
	}
	while(top)son[s[top-1]][1]=s[top],top--;
	//赋边权
	For(i,1,n){
		if(son[i][0])w[i][0]=a[son[i][0]]-a[i];
		if(son[i][1])w[i][1]=a[son[i][1]]-a[i];
	}
	//树上背包
	dfs(s[1]);
	
	printf("%lld",f[s[1]][m]);
	
	return 0;
}

尾声

如果你发现了问题,你可以直接回复这篇题解

如果你有更好的想法,也可以直接回复!

posted @   zsc985246  阅读(342)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示