Codeforces Round 892 (Div. 2) E

E的话一眼dp,然后观察一下方程,\(f[i][j]表示前i个位置已经选了长度为j的区间,且第i个位置已经被选上时,能够获得的最大值\)

\[f[i][j]=\displaystyle\max_{1\leq k\leq min(i,j)}(f[i-k][j-k]+calc(i-k+1,j))\\ calc(l,r)=|b_l-a_r|+|b_r-a_l| \]

这样的dp是\(O(n^2k)\)的,而\(1\leq k\leq n\leq 3000\),我们需要优化到\(O(n^2log(n))\)这个级别。

观察dp,考虑优化,正常情况下,优化的部分都是枚举k的部分,我们对每次转移分开考虑,假如我不需要再去枚举k,而是直接通过数据结构查询得到就可以了。

发现\(f[i-k][j-k]\)是一个很有规律的东西,转化为图形,就是在dp数值组成的表里面斜率为1的直线通过的点。而这个很明显是可以维护的,问题就在后面的\(calc\)的代价函数中

我们现在的\(f[i-k][j-k]\)可以被单独维护,导致我们必须枚举k的东西是calc中含有k的部分,假如这部分不存在,那么其实直接维护\(f[i-k][j-k]和后面剩下部分的\)\(max\)即可,对于绝对值也是随便处理,因为这个值其实是固定的。
而含有和k相关的部分,这导致先前使用过的,已经维护好的\(f[i-k][j-k]\)的最大值并不能直接套用,我们必须考虑新的一位和前面部分的结合而形成的绝对值的影响。

假如把绝对值去掉,是否能做呢?
可以。而且很好做。观察发现,其实\(calc\)函数中含有\(k\)的部分是和\(f[i-k][j-k]\)绑定的,这个其实从题目中对价值的描述中可以更好的看出来。也就是说,我们其实是直接维护了这个式子含有k的所有部分一同的最大值,这个很好,因为剩下的只和\(i,j\)相关的部分的值是确定的,也就是说我们需要的东西就直接\(O(1)\)维护好了。
而这个只是没有绝对值的情况,绝对值这个东西也不算难处理,直接分类讨论,拆开,我们两个绝对值一共4中情况。而事实上也不用分开维护,仔细考虑一下,加上绝对值只会让答案变大,所以直接取max,根本就可能取到不合法的情况的值。否则没法维护。这不是一个普通的偏序。这一点很巧妙。

不错的dp,分析这个代价函数的内容是情理之中,而这个代价函数中和\(f[i-k][j-k]\)强相关的部分是要从题目中理解得到的,这算是一个小难点。其实拆开绝对值的操作也是很合理的,想要优化这个dp,无非就是从状态和转移两方面下手,所有的dp都是这么优化的,而状态的优化其实是对应了前两个循环,这很显然很难办到,那就是加速转移。想要加速转移,那拆开这个绝对值就是必要的思路。

这边就给我提供了一个很好的思路,先假设绝对值不存在,然后再考虑。

还算简单的一个dp优化吧。这题居然能有2500。那9062E2居然只有2600。这两题之间的难度差距真心挺大。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
	ll a=0,b=1;char c=getchar();
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-'0';
	return a*b;
}
ll n,K,a[3021],b[3021],f[3021][3021];
ll Max[3021][3021][5];
ll s1[5]={0,+1,-1,+1,-1},s2[5]={0,-1,-1,+1,+1};
int main()
{
	ll T=read();
	while(T--)
	{
		n=read(),K=read();
		for(ll i=1;i<=n;i++)a[i]=read();
		for(ll i=1;i<=n;i++)b[i]=read();
		for(ll i=0;i<=n;i++)
		{
			for(ll j=0;j<=K;j++)
			{
				f[i][j]=0;
				for(ll k=1;k<=4;k++)
				Max[i][j][k]=-(1LL<<60);
			}
		}
		for(ll i=1;i<=n;i++)
		{
			for(ll j=1;j<=K;j++)
			{
				f[i][j]=f[i-1][j];
				for(ll k=1;k<=4;k++)
						Max[i][j][k]=max(Max[i-1][j-1][k],f[i-1][j-1]+a[i]*s1[k]+b[i]*s2[k]);
				for(ll k=1;k<=4;k++)
						f[i][j]=max(f[i][j],Max[i][j][k]-a[i]*s2[k]-b[i]*s1[k]);
			}
		}
		cout<<f[n][K]<<endl;
	}
	return 0;
}
posted @ 2024-04-25 11:59  HL_ZZP  阅读(5)  评论(0编辑  收藏  举报