【Luogu_P8118】 「RdOI R3.5」Mystery【Slope Trick】【DP】
P8118 「RdOI R3.5」Mystery
题目描述
给出一个长度为 nnn 的单调不降整数数列 {ai}\{a_i\}{ai} 和一个整数 kkk。
我们定义两个长度均为 ppp 的序列 {xi},{yi}\{x_i\},\{y_i\}{xi},{yi} 的「差异度」F(x,y,p)=∑i=1p∣xi−yi∣F(x,y,p)=\sum_{i=1}^p |x_i-y_i|F(x,y,p)=∑i=1p∣xi−yi∣。
现在对于每个整数 l∈[1,n]l \in [1,n]l∈[1,n],你都需要构造一个长度为 lll 的序列 {bl,i}\{b_{l,i}\}{bl,i}。满足对于任意 1≤i<l1\le i <l1≤i<l,bl,i+1≥bl,i+kb_{l,i+1}\ge b_{l,i}+kbl,i+1≥bl,i+k;且 F(a[1⋯l],bl,l)F(a_{[1\cdots l]},b_l,l)F(a[1⋯l],bl,l) 最小。其中 a[1⋯l]a_{[1\cdots l]}a[1⋯l] 表示 {ai}\{a_i\}{ai} 的长度为 lll 的前缀,即 {a1,a2,⋯ ,al}\{a_1,a_2,\cdots,a_l\}{a1,a2,⋯,al}。注意,bl,ib_{l,i}bl,i 没必要是整数。
输入格式
第一行输入两个整数 n,kn,kn,k。
第二行输入 nnn 个整数,代表 {ai}\{a_i\}{ai}。
第三行输入一个整数 TTT,代表答案输出方式。具体解释请参考「输出格式」。
输出格式
- 若 T=0T=0T=0,则输出 nnn 个整数,每个整数单独占一行。第 lll 行的整数代表 F(a[1⋯l],bl,l)F(a_{[1\cdots l]},b_l,l)F(a[1⋯l],bl,l)。
- 若 T=1T=1T=1,则你仅需输出一行一个整数,表示 F(a,bn,n)F(a,b_n,n)F(a,bn,n)。
输入输出样例 #1
输入 #1
5 2
2 3 4 5 6
0
输出 #1
0
1
2
4
6
输入输出样例 #2
输入 #2
6 2
1 1 4 5 6 8
0
输出 #2
0
2
2
3
4
5
输入输出样例 #3
输入 #3
6 2
1 1 4 5 6 8
1
输出 #3
5
输入输出样例 #4
输入 #4
20 4
4 6 7 9 19 21 30 32 33 35 49 50 58 67 75 77 78 89 91 91
0
输出 #4
0
2
5
10
10
12
12
14
17
22
22
25
25
25
25
27
30
30
32
36
说明/提示
样例解释
样例 #1
如下是一种可能的构造方案:
b1={2}b2={2,4}b3={1,3,5}b4={1,3,5,7}b5={0,2,4,6,8} \begin{aligned} b_1&=\{2\}\\ b_2&=\{2,4\}\\ b_3&=\{1,3,5\}\\ b_4&=\{1,3,5,7\}\\ b_5&=\{0,2,4,6,8\}\\ \end{aligned} b1b2b3b4b5={2}={2,4}={1,3,5}={1,3,5,7}={0,2,4,6,8}
样例 #2
如下是一种可能的构造方案:
b1={1}b2={0,2}b3={0,2,4}b4={0,2,4,6}b5={−1,1,3,5,7}b6={−1,1,3,5,7,9} \begin{aligned} b_1&=\{1\}\\ b_2&=\{0,2\}\\ b_3&=\{0,2,4\}\\ b_4&=\{0,2,4,6\}\\ b_5&=\{-1,1,3,5,7\}\\ b_6&=\{-1,1,3,5,7,9\}\\ \end{aligned} b1b2b3b4b5b6={1}={0,2}={0,2,4}={0,2,4,6}={−1,1,3,5,7}={−1,1,3,5,7,9}
样例 #3
同样例 #2,只不过 T=1T=1T=1,你只需要输出 F(a,b6,6)=5F(a,b_6,6)=5F(a,b6,6)=5 即可。
数据范围及约定
subtask分值n≤T=k,ai≤subtask 依赖1301000100−230105010613401061106− \def\arraystretch{1.5} \begin{array}{|c|c|c|c|c|c|} \hline \textbf{subtask} & \textbf{分值} & \bm{{n\le}} & \bm{{T=}} & \bm{{k,a_i\le}} & \textbf{subtask 依赖}\cr\hline 1 & 30 & 100 & 0 & 100 & -\cr\hline 2 & 30 & 10^5 & 0 & 10^6 & 1\cr\hline 3 & 40 & 10^6 & 1 & 10^6 & -\cr\hline \end{array} subtask123分值303040n≤100105106T=001k,ai≤100106106subtask 依赖−1−
对于 100%100\%100% 的数据,1≤n≤1061\le n \le 10^61≤n≤106,1≤k,ai≤1061\le k,a_i\le 10^61≤k,ai≤106,T∈{0,1}T\in\{0,1\}T∈{0,1}。
思路:
首先了解Slope Trick
本质上是一种一次分段函数的优化。
拿本题来举例(首先容易想到让aia_iai减去i∗ki*ki∗k将题目转化为通过最少次加减1使得序列单调不降),可是设fi,jf_{i,j}fi,j表示到了第iii位为jjj的最少次数。
可得fi,j=min(fi−1,k+∣ai−j∣),1≤k≤jf_{i,j}=min(f_{i-1,k}+|a_i-j|),1\le k\le jfi,j=min(fi−1,k+∣ai−j∣),1≤k≤j
前缀min优化,可得fi,j=min(fi,j−1,fi−1,j+∣ai−j∣)f_{i,j}=min(f_{i,j-1},f_{i-1,j}+|a_i-j|)fi,j=min(fi,j−1,fi−1,j+∣ai−j∣)
把它写成函数的形式,即令F(x)=fi,jF(x)=f_{i,j}F(x)=fi,j,G(x)=fi−1,jG(x)=f_{i-1,j}G(x)=fi−1,j,H(x)=∣ai−j∣H(x)=|a_i-j|H(x)=∣ai−j∣
得F(x)=G(x)+H(x)F(x)=G(x)+H(x)F(x)=G(x)+H(x)
因为G(x)G(x)G(x),H(x)H(x)H(x)都为一次分段单凹函数,所以F(x)F(x)F(x)也为一次分段单凹函数,那么接下来使用Slope Trick优化
对于一个一次分段凹函数,我们可以将它记录为在拐点处斜率的加减的形式,如
f(x)={−x,x<−1x=1,−1≤x≤1x,x>1f(x)=\left\{ \begin{aligned} {-x,x<-1} \\ {x=1,-1\le x \le 1} \\ {x,x>1} \end{aligned} \right.f(x)=⎩⎨⎧−x,x<−1x=1,−1≤x≤1x,x>1
那么它的记录就是-1,1
表示它在x=-1处斜率加1,在x=1处斜率加1。
如果要合并两个函数,就直接将两个函数的记录数组合并就可以了。
对于这个题目,维护一个凹的分段一次函数,且只有维护左半边,并保持函数的斜率最后一定为0,因为对于后半部分斜率上升的对答案不会有任何影响。
考虑当前函数中最后一段的斜率为0的位置ttt和aia_iai,若t≤ait \le a_it≤ai,则证明aia_iai在当前不需要任何操作,所以贡献为0
若ai<ta_i < tai<t,则将其升为ttt,产生t−ait-a_it−ai的贡献。
然后将H(x)H(x)H(x)加入函数中时,注意如果t≤ait \le a_it≤ai,则只用将H(x)H(x)H(x)的左半部分加入即可,即只用加一次aia_iai,否则加两次。
code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>#define ll long longusing namespace std;const ll MAXN = 1e6 + 5;ll n, k, ans, T;
ll a[MAXN];priority_queue<ll> q;int main() {scanf("%lld%lld", &n, &k);for(ll i = 1; i <= n; i ++) scanf("%lld", &a[i]), a[i] -= i * k;scanf("%lld", &T);for(ll i = 1; i <= n; i ++) {if(i == 1) {q.push(a[i]);if(!T) printf("0\n");continue;}if(a[i] >= q.top()) q.push(a[i]);else {ans += q.top() - a[i];q.push(a[i]), q.push(a[i]), q.pop();}if(!T) printf("%lld\n", ans);}if(T) printf("%lld", ans);return 0;
}