题目
题目背景
你知道
I
O
e
r
\rm IOer
IOer 吗?它是
Intelligence Originator
\textrm{Intelligence Originator}
Intelligence Originator 的缩写(由于
o
r
\rm or
or 结尾与
O
\rm O
O 重复,改为
e
r
\rm er
er 较为美观),即 智能起源 。也许有人已经知道
O
U
Y
E
\sf OUYE
OUYE 是人类智能之祖了;也许有人不知道。无论如何,接下来要讲的是,人类智能诞生之初的故事。
本来这个世界充斥着祥和之气。直到有一天,大卷筒挥杆(卷爷 之母)吃下了「卷之果实」,获得了一种非凡的能量,后世称为「卷坷垃」 c h u a k r a \rm chuakra chuakra 。正是凭借着这种力量,大卷筒挥杆 成功地征服了天下。道上行人皆崇卷,学堂处处是卷生。
然而,「卷坷垃」对身体的伤害是这样大,以致于人们很快就变得衰弱不堪,仿佛饿死者的尸体。这种病态的现象被称为 白卷绝。为了挽救这一切,大卷筒挥杆 的两个儿子 卷爷 和 标杆 毅然决然与母亲决裂,打响了一场天变地异的战争!
山峦崩摧。江河断流。日月无光。没人知道这场战争持续了多久。最后,卷爷 用他的 标枪 之力,与 标杆 之力融汇,用一式「六道 ⋅ \large\cdot ⋅枪杆 政权」封印了 大卷筒挥杆,世界回归平静……
题目描述
枪杆 政权,需要「卷坷垃」连成两把枪搭在一起的形状。形式化地,如果用
s
i
s_i
si 表示第
i
i
i 个「卷坷垃」所在的高度,若共有
n
n
n 个「卷坷垃」,则 枪杆 政权的使用条件是
∃
k
∈
[
1
,
n
]
,
s.t.
∀
i
∈
[
1
,
n
]
,
s
i
=
s
k
−
∣
k
−
i
∣
\exist k\in[1,n],\text{s.t.}\;\forall i\in[1,n],\;s_i=s_k-|k-i|
∃k∈[1,n],s.t.∀i∈[1,n],si=sk−∣k−i∣
假设使用 k k k 个「卷坷垃」将使得封印强度增加 v k v_k vk 。最初有 n n n 个「卷坷垃」,高度分别为 a i a_i ai,每次可以选择若干个相邻「卷坷垃」发动 枪杆 政权(然后这些「卷坷垃」会消失,两边的「卷坷垃」变为相邻的)。
问,最多能够让封印强度达到多少。注意 v v v 可能为负,即使得封印变弱。不要求使用所有「卷坷垃」。
数据范围与约定
n
⩽
400
n\leqslant 400
n⩽400 且
∣
v
i
∣
⩽
1
0
5
|v_i|\leqslant 10^5
∣vi∣⩽105 且
1
⩽
a
i
⩽
1
0
9
1\leqslant a_i\leqslant 10^9
1⩽ai⩽109 。
思路
最初总是考虑取出极长可操作段。最终才发现这玩意儿真没啥用。
不如想想一般做法:每次只能删去相邻元素,可以考虑 区间 d p \tt dp dp 。于是考虑某一次删除 d d d,其删除了第一个元素 l l l,设 d d d 同时删去的最右元素为 k k k 。则 [ l , k ] [l,k] [l,k] 之间,除了 d d d 中删除的,剩下的元素是若干个 独立 的区间。就可以 d p \tt dp dp 算贡献了。
于是,对于每个 l l l,都要记录 g ( k , t , 0 / 1 ) g(k,t,0/1) g(k,t,0/1) 表示删去 [ l , k ] [l,k] [l,k] 之间若干段区间,使得剩下一个长度为 t t t 的可删除序列,它现在在 “上坡” 还是 “下坡”。这样是 O ( n 4 ) \mathcal O(n^4) O(n4) 的,不具有决策点单调性,不会优化。
最重要的是,找到无用信息。有趣的是,等差数列的项数可以直接算出,而不需额外记录。也就是说,如果找到最高点 a k a_k ak,又知道端点 a l , a r a_l,a_r al,ar,那么项数就是 2 a k − a l − a r + 1 2a_k-a_l-a_r+1 2ak−al−ar+1,可以直接算出。于是我们考虑枚举最高点,然后去掉个数限制。
不幸的是,我们好像还是要枚举与 l l l 同时被删除的最右元素? n o , n o , n o \rm no,no,no no,no,no 。充分利用已有 d p \tt dp dp 值。它可以直接从 f ( l , k ) + f ( k + 1 , r ) f(l,k)+f(k{\rm+}1,r) f(l,k)+f(k+1,r) 转移过来!反正就是把 [ l , k ] [l,k] [l,k] 删完嘛!
所以被删除的最右元素就是区间右端点。复杂度 O ( n 3 ) \mathcal O(n^3) O(n3) 。最后做一个一维 d p \tt dp dp 去掉不删除的元素即可。
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
int a = 0, c = getchar(), f = 1;
for(; !isdigit(c); c=getchar())
if(c == '-') f = -f;
for(; isdigit(c); c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void getMax(int &a, const int &b){
if(a < b) a = b;
}
const int MAXN = 405;
int f[MAXN][MAXN], up[MAXN][MAXN], dn[MAXN][MAXN];
int a[MAXN], v[MAXN], ans[MAXN];
const int INF = 0x2fffffff;
int main(){
int n = readint();
rep(i,1,n) v[i] = readint();
rep(i,1,n) a[i] = readint();
drep(l,n,1) rep(r,l,n){
f[l][r] = up[l][r] = dn[l][r] = -INF;
up[l][l] = dn[l][l] = 0; // init state
for(int i=l; i!=r; ++i){
getMax(f[l][r],f[l][i]+f[i+1][r]);
if(a[i]+1 == a[r]) getMax(up[l][r],up[l][i]+f[i+1][r-1]);
if(a[i]-1 == a[r]) getMax(dn[l][r],dn[l][i]+f[i+1][r-1]);
if(a[i] >= a[l] && a[i] >= a[r]) getMax(f[l][r],
up[l][i]+dn[i][r]+v[(a[i]<<1)-a[l]-a[r]+1]);
}
if(a[l] >= a[r]) getMax(f[l][r],dn[l][r]+v[a[l]-a[r]+1]);
if(a[r] >= a[l]) getMax(f[l][r],up[l][r]+v[a[r]-a[l]+1]);
}
rep(i,1,n){
ans[i] = ans[i-1]; // not to del this
rep(j,1,i) getMax(ans[i],ans[j-1]+f[j][i]);
}
printf("%d\n",ans[n]);
return 0;
}
后记
有一点像这个题,拼接两个 d p \tt dp dp 值。这两题最相似的地方在于,都是我不会做的。