BZOJ1367 [Baltic2004]sequence题解(DP+单调性优化+堆)

版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/90605635

题目:BZOJ1367.
题目大意:给定一个长度为 n n 的序列 t t ,要求构造一个严格递增的序列 z z ,使得 s = i = 1 n z i t i s=\sum_{i=1}^{n}|z_i-t_i| 最小,并输出这个 s s .
1 n 1 0 6 1\leq n\leq 10^6 .

跟着道题差不多的还有三道题:POJ3666,CF713C,hihocoder1529.

首先考虑把 t i t_i 减去 i i 得到一个新的序列 t t ,那么原问题的严格递增就可以变为非严格递增.

考虑一个性质,必定存在一个最优的序列 z z 使得 z z 中所有出现过的数是 t t 中所有出现过的数的子集.

证明是不存在的,这很显然嘛…

于是我们可以设 f [ i ] f[i] 表示前 i i 个数,其中满足 z [ i ] = t [ i ] z[i]=t[i] 时最小的 s s .很容易得到方程:
f [ i ] = min j = 0 i 1 { f [ j ] + k = j + 1 i t i t k } f[i]=\min_{j=0}^{i-1}\{f[j]+\sum_{k=j+1}^{i}|t_i-t_k|\}

这个做法是 O ( n 3 ) O(n^3) 的,过不了此题.

所以考虑换一个状态,设 f [ i ] [ j ] f[i][j] 表示前 i i 个数,其中 z i = j z_i=j 时最小的 s s .容易得到方程:
f [ i ] [ j ] = min k = 0 j { f [ i 1 ] [ k ] + t i j } f[i][j]=\min_{k=0}^{j}\{f[i-1][k]+|t_i-j|\}

直接做的复杂度仍然是 O ( n 2 ) O(n^2) 的,依旧过不了此题.

考虑一下把 f [ i ] f[i] 看成关于 j j 的函数,容易通过数学归纳法证明图像是具有单峰性的,其中峰值为最小值:
在这里插入图片描述
如果是通过这个状态得到答案,答案为 min i = 0 t n { f [ n ] [ i ] } \min_{i=0}^{t_n}\{f[n][i]\} ,虽然这个函数具有凹凸性,但这并不好维护.

我们再设 F [ i ] [ j ] F[i][j] f [ i ] [ j ] f[i][j] 的前缀最小值,也就是说 F [ i ] [ j ] F[i][j] 表示 z i j z_i\leq j 时最小的 s s .容易得到另一个方程:
F [ i ] [ j ] = min k = 0 j { F [ i 1 ] [ k ] + t i k } = min k = 0 j { f [ i ] [ j ] } F[i][j]=\min_{k=0}^{j}\{F[i-1][k]+|t_i-k|\}=\min_{k=0}^{j}\{f[i][j]\}

现在答案就变成了简单的一个值 F [ n ] [ t n ] F[n][t_n] ,并且容易发现 F [ i ] F[i] 的图像就会呈现一个很优秀的单调性:
在这里插入图片描述
我们考虑从从 f [ i ] f[i] 转移到 f [ i + 1 ] f[i+1] 有什么变化,发现无非多了一个绝对值函数 t i j |t_i-j|
在这里插入图片描述
那么这一个增加的量对 f f 的函数图像会产生什么效果呢?显然是对所有横坐标在 t i t_i 前的线段斜率 1 -1 ,在 t i t_i 后的线段斜率 + 1 +1 .

要维护这个东西可不好搞啊…考虑这个玩意会对 F F 的图像造成什么影响.

f [ i ] f[i] 图像最低点的横坐标为 x i x_i ,则对于 F [ i ] F [ i + 1 ] F[i] \Rightarrow F[i+1] 我们需要分两类讨论:
1.若 x i t i + 1 x_i\leq t_{i+1} ,容易发现在 t i + 1 t_{i+1} 之前的所有线段斜率 1 -1 ,而 t i + 1 t_{i+1} 之后的线段斜率仍然为 0 0 ,纵坐标为 F [ i ] [ x i ] F[i][x_i] .用图像来表示就是:
在这里插入图片描述
2.若 x i > t i + 1 x_i>t_{i+1} ,容易发现现在还是给 t i + 1 t_{i+1} 前的线段斜率 1 -1 ,但是只给 t i + 1 t_{i+1} 后斜率小于 0 0 的线段斜率 + 1 +1 ,等于 0 0 的依旧为 0 0 ,用图像来表示就是:
在这里插入图片描述
虽然我知道这个例子不是很好,但是我懒得换了QAQ…

然后这个变换会使得图像中的最低点的纵坐标也就是答案增加了多少呢?不难发现一定是最左边的最低点的横坐标减去 t i + 1 t_{i+1} ,也就是一定 f [ i + 1 ] [ x i + 1 ] f[i+1][x_{i+1}] 一定由 f [ i ] [ x i ] f[i][x_i] 转移过去.

为什么呢?可以观察一下 F [ i ] F[i] 的图像,发现 x i x_i 之前的线段斜率一定小于等于 1 -1 ,而 + 1 +1 后这个点必然还是最低点(只是不是最左边的了),所以这样转移一定最优.

知道了这些后,我们该干什么呢?

首先 F [ i ] F[i] 图像上所有线段的斜率必然是在区间 [ 2 i , 0 ] [-2i,0] 上的整数,这可以通过上面的斜率变换得到.

现在我们只需要维护每一条斜率不同的线段即可,但是显然这样很麻烦,考虑只维护每一个不同斜率的左端点的横坐标.

只需要对横坐标维护一个优先队列即可,可以通过这个优先队列维护我们上面需要的所有操作,具体实现可以看代码.

时间复杂度 O ( n log n ) O(n\log n) .

代码如下:

//再来解释一下这个神奇的优先队列好了.
//其实这个优先队列中,优先级最高的斜率为0,第2的斜率为1...第i的斜率为i-1.
//也就是说如果有一个斜率i不存在,我们也要存,即第i+1和第i+2个元素相等即可.
#include<bits/stdc++.h>
  using namespace std;
 
#define Abigail inline void
typedef long long LL;
 
priority_queue<int>q;
int n;
LL ans;
 
Abigail into(){
  scanf("%d",&n);
  int a;
  for (int i=1;i<=n;++i){
    scanf("%d",&a);a-=i;
    q.push(a);
    if (a<q.top()) ans+=(LL)q.top()-a,q.pop(),q.push(a);
  }
}
 
Abigail outo(){
  printf("%lld\n",ans);
}
 
int main(){
  into();
  outo();
  return 0;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/90605635