写出转移方程:\(f_i\) 表示前 \(i\) 天且第 \(i\) 天必须跑的最大能量值。\(g_i=\max\limits_{j=1}^i\{f_j\}\)。初值 \(f_0=g_0=0\)。
对于转移方程,考虑枚举最后一段跑的段是从哪里开始的:\(f_i=\displaystyle\max_{j=i-k+1}^i(g_{j-2}+prize(j,i)-(j-i+1)\times d)\)。其中 \(prize(j,i)\) 是 \([j,i]\) 跑获得的奖励。
注意如果 \(j=1\) 要特判。
这个转移方程是 \(O(n^2m)\) 的。我们发现,每一个跑段的起终点一定是某个挑战的起终点,所以我们可以离散化。
(注意离散化后还要数组对应到原来坐标,注意离散化后的转移可能有两种:① 上一个离散点就在前一个 ② 上一个点不相邻)
然后我们发现这玩意可以用线段树优化:线段树第 \(i\) 个结点,表示当前的决策点为 \(i\) 时的值。
具体而言:
for (ll i = 1, p = 1; i <= cur; i++) {
while (p < i && rev[i] - rev[p] + 1 > k) //p指向当前第一个在rev[i]-k+1之后的
p++;
for (auto ch: chl[i])
st.mdf(1, 1, st.sz + 1, 1, ch.l + 1, ch.v);
ll tmp = (rev[i - 1] == rev[i] - 1 ? max(0ll, i - 2) : i - 1); //找到上一个决策点
st.mdf(1, 1, st.sz + 1, i, i + 1, g[tmp] + (rev[i] - 1) * d);
f[i] = st.qry(1, 1, st.sz + 1, p, i + 1) - rev[i] * d;
g[i] = max(g[i - 1], f[i]);
}
\(rev_i\) 是离散化后的原坐标。
当 \(now\) 右移一个,首先要枚举所有右端点是 \(now\) 的挑战,然后对 \([lft,now]\) 进行区间加 \(prize\)。
对位置 \(now\) 的决策设初值:\(g[now-1/now-2]+(rev[now]-1)\times d\)。
\(f[i]=st.qry(p,i+1)-rev[i]\times d\)。
为什么?我们把 \(f\) 的转移方程做变形:\(f_i=\max(g[j-1]+prize(j,i)-(i-j+1)\times d)\),其中 \(prize(j,i)\) 的累加用了线段树,而 \(-(i-j+1)\times d=(j-1)\times d-i\times d\)。
所以我们在每个决策位置 \(j\) 都预先 \(+(j-1)\times d\),算 \(f_i\) 的时候只需要求 \(\max\) 再减去就行了。
(有点像斜率优化:拆式子,费用提前计算)
还有,数据卡常。不能 map
离散化,要用 sort
。