Solution - P4597 序列 sequence

P4597 序列 sequence

idea:

贪心?大根堆, DPDP 分析(不会)

解决方案:

首先,有一个显然的 dpdp 方法:

fi,jf_{i,j} 表示前 ii 个固定,第 ii 个放了第 jj 大的数的最小花费。 BjB_j 表示第 jj 大的数的值。有 dpdp 方程:

fi,j=minkj{fi1,k}+BjAif_{i,j} = \min_{k\leq j} \{ f_{i-1,k}\} + \mid B_j - A_i\mid

这玩意求个前缀最小值就可以 O(n2)O(n^2) 了,但是通不过这道题。

然后去看题解,发现大佬都在分析凸包什么的,看不懂,只能看一个稍微平易近人的分析方法(类似于反悔贪心)。

假设现在加入一个数 xx ,已经搞好的数列的最大值为 yy 。那么如果 xyx\geq y ,我们是不需要操作的。如果 x<yx < y ,那么我们可以在 [x,y][x,y] 中找到一个值 zz ,让 xxyy 都等于 zz 。显然最优是让 x,yx,y 都降到 xx

但这样是有问题的,因为 yy 降到 xx 后,前面的数列就不再单调不降了。那为何这样做是对的呢?我们来看看:

现在数列长这样:

1
2
3
4
5
5      .
4
3 .
2
1 .

现在飞进来一个 22 ,那么我们会想把数列变成这样:

1
2
3
4
5
5
4
3 . . .
2
1 .

花费了 33 。但要是再飞进来一个 22 ,我们就想:要是当年把第三个和第四个数都降到 22 就好了,这样可以在花费相同的情况下使最大值小一点。

目标是这样的:

1
2
3
4
5
5
4
3
2 . . . .
1 .

实际上只需要花费 44 。如果我们不管数列是否合法的话,我们会这样:

1
2
3
4
5
5      .
4
3 .
2
1 .

变成:

1
2
3
4
5
5
4
3 .
2 . .
1 .

变成:

1
2
3
4
5
5
4
3
2 . . . .
1 .

也就是说,这么做不仅可以使当前贡献计算正确,还可以为未来谋取最优解。

十行 CodeCode

1
2
3
4
5
6
7
8
9
10
#include<bits/stdc++.h>
using namespace std;typedef long long LL;
priority_queue<LL> q;LL ans;
signed main(){
int n;cin>>n;while (n--){
int x;cin>>x;q.push(x);
if (q.top()>x) ans+=q.top()-x,q.pop(),q.push(x);
}
return cout<<ans,0;
}