洛谷题面传送门

u1s1 在我完成这篇题解之前,全网总共两篇题解,一篇使用的平衡树,一篇使用的就是这篇题解讲解的这个做法,但特判掉了一个点,把特判去掉在 BZOJ 上会 WA 一个点。

两篇题解都异常简略,前一篇题解甚至只有代码没有说明,所以这里我来写篇比较详细题解造福人类了(大雾

建议没做过这题的同学先看看这道题,看题解区第一个做法,对这题有比较大的启发。

注:下文中分别用 \(a,b,l,r\) 代替题面中的 \(X,Y,A,B\)。

首先关于这题咱们可以想到一个非常 naive 的 DP,\(dp_{i,j}\) 表示考虑到前两个数,\(b_i=j\) 的 \(\sum\limits_{k=1}^i|a_k-b_k|\)。那么显然有 DP 转移 \(dp_{i,j}=\min\limits_{k=l}^rdp_{i-1,j-k}+|a_i-j|\)。一脸不好直接维护的样子。不过注意到绝对值函数是一个下凸函数,因此我们猜测对于一个固定的 \(i\),\(dp_{i,j}\) 随 \(j\)​ 的增大也是一个下凸函数,事实也的确如此,证明大概可以归纳,可能需要一些分类讨论,这里不再赘述,留给读者自己思考。

我们还可以注意到,每一轮中每条直线斜率变化的绝对值最多为 \(1\)​​,也就是说任意时刻,下凸壳中直线斜率绝对值的最大值顶多为 \(n\)​​,因此我们考虑维护 \(2n+2\)​​ 个分界点的横坐标,具体来说,记 \(x_i\)​​ 为第 \(i\)​​ 个分界点,那么以凸包上横坐标分别为 \(x_i,x_{i+1}\)​​ 的点组成线段刚好是凸包上斜率为 \(i-n-2\)​​ 的直线,当然也有可能 \(x_i=x_{i+1}\)​​,此时凸包上不存在斜率为 \(i-n-2\)(说白了就是记录凸包拐点的横坐标)。

接下来考虑加入一个 \(a_i\)​​ 后会对凸包产生怎样的影响。我们考虑将每一轮转移的过程分成两部分:\(dp_{i,j}=\min\limits_{k=l}^rdp_{i-1,j-k}\)​ 和 \(dp_{i,j}:=dp_{i,j}+|a_i-j|\)​,首先对于第一部分而言,显然在凸包上存在一些分界点,满足分界点前面单调不增,分界点之后单调不降,不难发现这些分界点组成的集合就是斜率为 \(0\)​ 的直线。假设斜率为 \(0\)​ 的直线的两个端点横坐标为 \(u,v(u,v)\)​,那么不难发现对于 \(j\le u+l\),由于 \(u\) 前面单调递减,因此我们肯定会尽量选择 \(j\) 与接近的点转移,即 \(dp_{i,j-l}\),因此我们可以得到,进行第一步操作后,凸包上 \(u\) 前面的点都会向右平移 \(l\) 格,同理在 \(v+r\) 后面的点 \(j\),我们肯定会尽量选离 \(j\) 远的点的即 \(j-r\),也就是说 \(v\)​ 后面的点肯定会向右平移 \(r\) 格,至于中间的部分……那显然还是一条水平的直线咯(

第二部分就相对比较简单了,不难发现 \(f(x)=|a_i-x|\) 在 \((-\infty,a_i)\) 中是斜率为 \(-1\) 的直线,\((a_i,\infty)\)​ 中是斜率为 \(1\) 的直线,因此 \(a_i\)​ 前面的部分的直线的斜率会减 \(1\),后面的部分斜率会加 \(1\)。这样我们可以看作是在 \(a_i\) 处插入了两个断点。

考虑怎样维护这个东西,我们开两个堆 \(L,R\) 分别存储斜率为 \(0\) 的直线前面和后面的断点,那么时刻 \(i\) 时显然 \(L\) 应当为存储的是最小的 \(i\) 个断点,\(R\) 应当存储最大的 \(i\) 个断点,但有时并不一定真的存储的就是最小/最大的 \(i\) 个断点,这时候我们就要进行调整,具体方法就是取出 \(L\) 中最大的元素 \(x\) 和最小的元素 \(y\),如果 \(x>y\) 就将 \(x\) 插入 \(R\),\(y\) 插入 \(L\),否则 break。还有一个问题就是如何处理坐标平移,具体方法就在 \(i\) 时刻是将 \(x\) 插入 \(L\) 时我们不插入 \(x\) 本身,而是插入 \(x-(i-1)l\),这样在 \(i’\) 时刻这个点真正的坐标就是 \(x+i’l\),不难发现这里用了差分的思想,在统计每个点有多少个时刻满足 xxxx 条件时也用到了类似的思想。

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

注意加一些特判,否则在 BZOJ 上会 WA 一个点,进而取得 \(0\) 分的好成绩(London Fog

const int MAXN=5e5;
int n,mx,l,r,a[MAXN+5],b[MAXN+5];ll ext=0;
priority_queue<ll> L;
priority_queue<ll,vector<ll>,greater<ll> > R;
//void prt(auto q){
// while(!q.empty()) printf("%d ",q.top()),q.pop();
// printf("\n");
//}
int main(){
scanf("%d%d%d%d",&n,&mx,&l,&r);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]<1ll*(i-1)*l+1){
ext+=abs(a[i]-(1ll*(i-1)*l+1));
a[i]=1ll*(i-1)*l+1;
}
}
for(int i=1;i<=n;i++){
L.push(a[i]-1ll*(i-1)*l);
R.push(a[i]-1ll*(i-1)*r);
while(1){
ll x=L.top();x+=1ll*(i-1)*l;
ll y=R.top();y+=1ll*(i-1)*r;
// printf("%lld %lld\n",x,y);
if(x<=y) break;L.pop();R.pop();
L.push(y-1ll*(i-1)*l);R.push(x-1ll*(i-1)*r);
} b[i]=L.top()+1ll*(i-1)*l;
// prt(L);prt(R);
} ll res=0;chkmin(b[n],mx);
chkmax(b[n],1ll*l*(n-1)+1);//be careful
for(int i=n-1;i;i--){
if(b[i]<b[i+1]-r) b[i]=b[i+1]-r;
if(b[i]>b[i+1]-l) b[i]=b[i+1]-l;
}
// if(n==8888) for(int i=1;i<=n;i++) printf("%d%c",b[i]," \n"[i==n]);
for(int i=1;i<=n;i++) res+=abs(a[i]-b[i]);
printf("%lld\n",res+ext);
return 0;
}

洛谷 P4272 - [CTSC2009]序列变换(堆)的更多相关文章

  1. 【洛谷P1483】序列变换

    题目大意:给定一个长度为 N 的序列,有 M 个操作,支持将下标为 x 的倍数的数都加上 y,查询下标为 i 的元素的值. 题解:由于查询操作很少,相对的,修改操作很多.若直接模拟修改操作,即:枚举倍 ...

  2. 洛谷 P1628 合并序列

    洛谷 P1628 合并序列 题目传送门 题目描述 有N个单词和字符串T,按字典序输出以字符串T为前缀的所有单词. 输入格式 输入文件第一行包含一个正整数N: 接下来N行,每行一个单词,长度不超过100 ...

  3. 洛谷 P5470 - [NOI2019] 序列(反悔贪心)

    洛谷题面传送门 好几天没写题解了,写篇题解意思一下(大雾 考虑反悔贪心,首先我们考虑取出 \(a,b\) 序列中最大的 \(k\) 个数,但这样并不一定满足交集 \(\ge L\) 的限制,因此我们需 ...

  4. [洛谷P1032] 字串变换

    洛谷题目链接:字串变换 题目描述 已知有两个字串 A, B 及一组字串变换的规则(至多6个规则): A1 -> B1 A2 -> B2 规则的含义为:在 A$中的子串 A1 可以变换为 B ...

  5. BZOJ 1500 洛谷2042维护序列题解

    BZ链接 洛谷链接 这道题真是丧心病狂.... 应该很容易就可以看出做法,但是写代码写的....... 思路很简单,用一个平衡树维护一下所有的操作就好了,重点讲解一下代码的细节 首先如果按照常规写法的 ...

  6. 【洛谷 P1631】 序列合并 (堆)

    题目链接 直接暴力搞是\(n\)方的复杂度.\(n^2\)个数选\(n\)个最小的,容易想到堆. 我们堆里记录两个信息:到\(A\)数组哪个位置了,到\(B\)数组哪个位置了, 我直接把这两个信息存在 ...

  7. 【洛谷P1963】[NOI2009]变换序列(二分图匹配)

    传送门 题意: 现有一个\(0\)到\(n-1\)的排列\(T\),定义距离\(D(x,y)=min\{|x-y|,N-|x-y|\}\). 现在给出\(D(i, T_i)\),输出字典序最小的符合条 ...

  8. 洛谷P4165 [SCOI2007]组队(排序 堆)

    题意 题目链接 Sol 跟我一起大喊:n方过百万,暴力踩标算! 一个很显然的思路是枚举\(H, S\)的最小值算,复杂度\(O(n^3)\) 我们可以把式子整理一下,变成 \[A H_i + B S_ ...

  9. 洛谷P3378 【模板】堆

    P3378 [模板]堆 160通过 275提交 题目提供者HansBug 标签 难度普及- 提交  讨论  题解 最新讨论 经实际测试 堆的数组开3000- 题目有个问题 为什么这个按课本堆标准打的- ...

随机推荐

  1. 【数据结构】<栈的应用>回文判断

    通过栈与队列相关内容的学习,我们知道,栈是"先进后出"的线性表,而队列是"先进先出"的线性表.可以通过构造栈与队列来实现在这一算法.将要判断的字符序列依次压栈和 ...

  2. F1西班牙大奖赛-加泰罗尼亚赛道地图及简介

    背景 银石双赛结束,第二轮三连赛的最后一场将转战西班牙,第50届F1西班牙大奖赛将于本周末(正赛2020-08-15)在加泰罗尼亚赛道上演. 作为近年来F1承办季前测试的赛道,所有人都对这里再熟悉不过 ...

  3. 395.至少有 K 个重复字符的最长子串

    题目 给你一个字符串 s 和一个整数 k ,请你找出 s 中的最长子串, 要求该子串中的每一字符出现次数都不少于k .返回这一子串的长度. 示例 1: 输入:s = "aaabb" ...

  4. Java:判断是否相等小记

    Java:判断是否相等小结 对 Java 中的判断是否相等,即判断两数/两对象是否相等,做一个微不足道的小小小小记 == 判断 对于基本类型和引用类型 == 的效果是不同的,如下: 基本类型:比较的值 ...

  5. win10安装git fatal: open /dev/null or dup failed: No such file or directory错误解决方法

    原因看大家意思应该是 非即插即用驱动文件null.sys问题. 网上有很多方案.最后试了一个可行的. 替换  windows/system32/drivers/null.sys为网盘中的文件即可. 链 ...

  6. [no code][scrum meeting] Beta 10

    $( "#cnblogs_post_body" ).catalog() 例会时间:5月25日15:00,主持者:伦泽标 下次例会时间:5月26日11:30,主持者: 一.工作汇报 ...

  7. BUAA_2020_软件工程_热身作业

    项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任建) 这个作业的要求在哪里 热身作业要求 我在这个课程的目标 了解软件工程的技术,掌握工程化开发的能力 这个作业在哪个具体方面 ...

  8. 2020BUAA软工结伴项目作业

    2020BUAA软工结伴项目作业 17373010 杜博玮 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 结伴项目作业 我在这个课程的目标是 学 ...

  9. mongodb的简单查询

    此篇文章简单的记录一下mongodb 的简单查询操作. 一.数据准备: db.persons.insertMany([ {'userId':1,name:'张三','age':20,'scores': ...

  10. 热身训练2 Another Meaning

    题目来源 简要题意: 众所周知,在许多情况下,一个词语有两种意思.比如"hehe",不仅意味着"hehe",还意味着"excuse me". ...