洛谷 P4272 - [CTSC2009]序列变换(堆)
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]序列变换(堆)的更多相关文章
- 【洛谷P1483】序列变换
题目大意:给定一个长度为 N 的序列,有 M 个操作,支持将下标为 x 的倍数的数都加上 y,查询下标为 i 的元素的值. 题解:由于查询操作很少,相对的,修改操作很多.若直接模拟修改操作,即:枚举倍 ...
- 洛谷 P1628 合并序列
洛谷 P1628 合并序列 题目传送门 题目描述 有N个单词和字符串T,按字典序输出以字符串T为前缀的所有单词. 输入格式 输入文件第一行包含一个正整数N: 接下来N行,每行一个单词,长度不超过100 ...
- 洛谷 P5470 - [NOI2019] 序列(反悔贪心)
洛谷题面传送门 好几天没写题解了,写篇题解意思一下(大雾 考虑反悔贪心,首先我们考虑取出 \(a,b\) 序列中最大的 \(k\) 个数,但这样并不一定满足交集 \(\ge L\) 的限制,因此我们需 ...
- [洛谷P1032] 字串变换
洛谷题目链接:字串变换 题目描述 已知有两个字串 A, B 及一组字串变换的规则(至多6个规则): A1 -> B1 A2 -> B2 规则的含义为:在 A$中的子串 A1 可以变换为 B ...
- BZOJ 1500 洛谷2042维护序列题解
BZ链接 洛谷链接 这道题真是丧心病狂.... 应该很容易就可以看出做法,但是写代码写的....... 思路很简单,用一个平衡树维护一下所有的操作就好了,重点讲解一下代码的细节 首先如果按照常规写法的 ...
- 【洛谷 P1631】 序列合并 (堆)
题目链接 直接暴力搞是\(n\)方的复杂度.\(n^2\)个数选\(n\)个最小的,容易想到堆. 我们堆里记录两个信息:到\(A\)数组哪个位置了,到\(B\)数组哪个位置了, 我直接把这两个信息存在 ...
- 【洛谷P1963】[NOI2009]变换序列(二分图匹配)
传送门 题意: 现有一个\(0\)到\(n-1\)的排列\(T\),定义距离\(D(x,y)=min\{|x-y|,N-|x-y|\}\). 现在给出\(D(i, T_i)\),输出字典序最小的符合条 ...
- 洛谷P4165 [SCOI2007]组队(排序 堆)
题意 题目链接 Sol 跟我一起大喊:n方过百万,暴力踩标算! 一个很显然的思路是枚举\(H, S\)的最小值算,复杂度\(O(n^3)\) 我们可以把式子整理一下,变成 \[A H_i + B S_ ...
- 洛谷P3378 【模板】堆
P3378 [模板]堆 160通过 275提交 题目提供者HansBug 标签 难度普及- 提交 讨论 题解 最新讨论 经实际测试 堆的数组开3000- 题目有个问题 为什么这个按课本堆标准打的- ...
随机推荐
- Sequence Model-week3编程题2-Trigger Word Detection
1. Trigger Word Detection 我们的触发词将是 "Activate.".每当它听到你说 "Activate.",它就会发出 "c ...
- Noip模拟73 2021.10.10
老妈送来了防寒补给就很棒,再也不用晚上盖两层毛巾被了,再也不用担心晚上自动把毛巾被$split$了 还有一些好吃的耶叶 T1 小L的疑惑 考场上疑惑的切掉了 直接把$a$排序然后处理前缀和的过程中判断 ...
- 2021.8.24考试总结[NOIP47]
T1 prime 发现只需筛小于等于$mid(\sqrt r,k)$的质数,之后用这些质数筛掉区间内不合法的数即可. $code:$ 1 #include<bits/stdc++.h> 2 ...
- TCP/IP参考模型(应用层、传输层、网际层、网络接口层)、五层参考模型(应用层、传输层、网络层、数据链路层、物理层)、OSI与TCP/IP参考模型比较
文章转自:https://blog.csdn.net/weixin_43914604/article/details/104597450 学习课程:<2019王道考研计算机网络> 学习目的 ...
- CSP-S 2021 爆零记
前言 本人今年高二蒟蒻OIer,高一刚刚接触OI. 感觉可能要直接退役了555~ 希望还有机会靠NOIP翻盘 Day - 暑假 为了备战CSP提前返校,与xzh一起划水,总之刷了不少题,我也大受震撼 ...
- caffe的idx1-ubyte和idx1-ubyte文件转换成图片文件和文本文件
train-images-idx3-ubyte : training set images train-labels-idx1-ubyte : training set labels t10k-i ...
- hdu 1847 Good Luck in CET-4 Everybody! (简单博弈)
题意: n张牌,双方轮流抓取.每人每次抓取的牌数必须是2的幂次(1,2,4,8...). 最后抓完的人胜. 思路 : 考虑剩3张牌,后手胜. 考虑3的倍数.假设先抓者当轮抓2x 张,2x %3等于1或 ...
- linux 内核源代码情景分析——linux 内核源码中的汇编语言代码
1. 用汇编语言编写部分核心代码的原因: ① 操作系统内核中的底层程序直接与硬件打交道,需要用到一些专用的指令,而这些指令在C语言中并无对应的语言成分: ② CPU中的一些特殊指令也没有对应的C语言成 ...
- Linux&c 文件操作,线程进程控制,网络编程,简单知识点梳理
一:文件操作 在linux下,一切皆文件,目录是文件,称为目录文件,内容是该目录的目录项(但是目录只有内核可以编辑,超级用户也不可以编辑),设备也是设备文件,在/dev存放的就是一些设备文件,linu ...
- Jmeter分布式 (三)
一.什么是分布式测试 分布式测试是指通过局域网和Internet,把分布于不同地点.独立完成特定功能的测试计算机连接起来,以达到测试资源共享.分散操作.集中管理.协同工作.负载均衡.测试过程监控等目的 ...