CF 1859E
题意:给你两个长度为
n
n
n的数组
a
a
a,
b
b
b,选择一些点对
(
l
i
,
r
i
)
(l_i,r_i)
(li,ri)。
每个点对对答案产生贡献
a
b
s
(
b
l
i
−
a
r
i
)
+
a
b
s
(
b
r
i
−
a
l
i
)
abs(b_{l_i}-a_{r_i})+abs(b_{r_i}-a_{l_i})
abs(bli−ari)+abs(bri−ali) 。
任意两点对
(
l
i
,
r
i
)
(
l
j
,
r
j
)
(l_i,r_i)(l_j,r_j)
(li,ri)(lj,rj)间必须满足
r
i
<
l
j
r_i<l_j
ri<lj或者
r
j
<
l
i
r_j<l_i
rj<li
点对长度为
r
i
−
l
i
+
1
r_i-l_i+1
ri−li+1,求选取点对总长度为
k
k
k时,最大总贡献为多少
朴素写法直接枚举每个当前选到的点,然后枚举当前长度,对当前点进行选与不选的决策,选:直接枚举点对中的 l l l 转移即可;不选:直接从前一个点转移过来,复杂度 O ( n 3 ) O(n^3) O(n3)
这种暴力的做法时间不太够,因为我们在确定点对中 r i r_i ri时,转移时我们必须知道 l i l_i li为多少,才能算出这个点对的贡献。
状态优化:因为这个贡献很特殊,它是绝对值型的,比如我们考虑 a b s ( x − y ) abs(x-y) abs(x−y),可以转换为 m a x ( x − y , y − x ) max(x-y,y-x) max(x−y,y−x),发现 x , y x,y x,y的符号可以多开一个状态来表示,暴力枚举符号,因为绝对值取max所以最后的答案一定最优
所以可以这样做 f x , y , l f_{x,y,l} fx,y,l 表示当前长度为 l l l, ( b l i − a r i ) (b_{l_i}-a_{r_i}) (bli−ari) 的符号状态为 x x x , ( b r i − a l i ) (b_{r_i}-a_{l_i}) (bri−ali) 的符号状态为 y y y 且已选取 l i l_i li暂未选择 r i r_i ri时的最大贡献, g l g_l gl表示选择完 r i r_i ri后的最大贡献,也就是统计的答案,因为贡献的两个符号状态我们可以在 f f f转移时就确定,所以 g g g作为答案数组可以省去这两个状态
最后只要枚举当前点,做三件事:
1.选择当前点并作为新的
l
i
l_i
li,确定
x
,
y
x,y
x,y,计算
a
l
i
a_{l_i}
ali和
b
l
i
{b_{l_i}}
bli的贡献
2.不选择当前点,延续之前确定了的
l
i
l_i
li和
x
,
y
x,y
x,y
3.选择当前点作为
r
i
r_i
ri,计算贡献
void solve(){
std::vector f(2,std::vector(2,std::vector<ll>(K+1,-inf)));
std::vector<ll> g(K+1);
g[0]=0;
for (int i=1;i<=n;i++){
for (int x=0;x<2;x++){
for (int y=0;y<2;y++){
for (int l=K;l>=1;l--){
f[x][y][l]=f[x][y][l-1];
}
}
}
for (int x=0;x<2;x++){
for (int y=0;y<2;y++){
for (int l=0;l<K;l++){
f[x][y][l+1]=std::max(f[x][y][l+1],g[l]+(x?-1:1)*b[i]+(y?1:-1)*a[i]);
}
}
}
for (int x=0;x<2;x++){
for (int y=0;y<2;y++){
for (int l=0;l<=K;l++){
g[l]=std::max(g[l],f[x][y][l]+(y?-1:1)*b[i]+(x?1:-1)*a[i]);
}
}
}
}
std::cout<<g[K]<<"\n";
}