PS:参考了黄源河的论文《左偏树的特点及其应用》

题目描述:给定一个整数序列\(a_1, a_2, … , a_n\),求一个递增序列\(b_1 < b_2 < … < b_n\),使得序列\(a_i\)和\(b_i\)的各项之差的绝对值之和 \(|a_1 - b_1| + |a_2 - b_2| + … + |a_n - b_n|\) 最小。

不难发现两条性质:

①:若原序列a满足\(a_1 < a_2 < … < a_n\),显然最优情况为\(b_i=a_i\)

②:若原序列a满足\(a_1 > a_2 > … > a_n\),显然最优情况为\(b_{mid}=x\)(x为a中位数)

有了上述的两种情况,不难发现,整个a序列是尤一些单调区间组成。

所以我们可以将原序列a拆成若干个单调区间,最后再将答案合并。

那两段区间的答案怎么合并呢?

我们可以重新找一个中位数来合并即可。

不断的找中位数,不难想到这道题,可是那道题是一个一个加入进堆,而现在我们要解决的是将两个堆合并来找中位数,直接上二叉堆合并复杂度为\(O(n)\),所以不难想到可并堆(这里使用左偏树)。

假设我们已经找到前k个数的最优解,队列中有\(cnt\)段区间,每段区间最优解为\(w_1,w_2,…,w_{cnt}\),现在要加入\(a_{k+1}\),并更新队列。

首先把\(a_{k+1}\)加入队尾,令\(w_{cnt+1}=a_{k+1}\),如果\(w_{cnt}>w_{cnt+1}\),就将最后两个区间合并,并找出新区间的最优解。重复上述过程,直至满足\(w\)单调递增。

注意:题目要求的是一个递增序列b,可以用减下标来实现(即输入时把每个数都减去对应下标,输出时加上),这样就可以将递增序列转化成不下降序列,这样就可以保证每一段区间的序列b不一样了(原本有很多连续一段区间全是中位数,不能保证递增)。

代码如下:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define il inline
  4. #define re register
  5. #define debug printf("Now is Line : %d\n",__LINE__)
  6. #define file(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
  7. #define ll long long
  8. #define mod 1000000007
  9. il int read()
  10. {
  11. re int x=0,f=1;re char c=getchar();
  12. while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
  13. while(c>='0'&&c<='9') x=x*10+c-48,c=getchar();
  14. return x*f;
  15. }
  16. #define _ 1000006
  17. int n,dis[_],ch[_][2],cnt;
  18. ll a[_],b[_],ans;
  19. struct node{int root,ls,rs,size,val;}e[_];
  20. il int merge(int x,int y)
  21. {
  22. if(!x||!y) return x+y;
  23. if(a[x]<a[y]||(a[x]==a[y]&&x>y)) swap(x,y);
  24. ch[x][1]=merge(ch[x][1],y);
  25. if(dis[ch[x][0]]<dis[ch[x][1]]) swap(ch[x][0],ch[x][1]);
  26. dis[x]=dis[ch[x][1]]+1;
  27. return x;
  28. }
  29. int main()
  30. {
  31. n=read(); dis[0]=-1;
  32. for(re int i=1;i<=n;++i) a[i]=read()-i;
  33. for(re int i=1;i<=n;++i)
  34. {
  35. e[++cnt]=(node){i,i,i,1,a[i]};
  36. while(cnt>1&&e[cnt].val<e[cnt-1].val)
  37. {
  38. --cnt;
  39. e[cnt].root=merge(e[cnt].root,e[cnt+1].root);
  40. e[cnt].size+=e[cnt+1].size;
  41. e[cnt].rs=e[cnt+1].rs;
  42. while(e[cnt].size*2>e[cnt].rs-e[cnt].ls+2)
  43. {
  44. --e[cnt].size;
  45. e[cnt].root=merge(ch[e[cnt].root][0],ch[e[cnt].root][1]);
  46. }
  47. e[cnt].val=a[e[cnt].root];
  48. }
  49. }
  50. for(re int i=1;i<=cnt;++i)
  51. {
  52. for(re int j=e[i].ls;j<=e[i].rs;++j)
  53. {
  54. b[j]=e[i].val;
  55. ans+=abs(a[j]-b[j]);
  56. }
  57. }
  58. printf("%lld\n",ans);
  59. for(re int i=1;i<=n;++i) printf("%lld ",b[i]+i);
  60. return 0;
  61. }

[BOI2004]Sequence 数字序列(左偏树)的更多相关文章

  1. 洛谷P4331 [BOI2004] Sequence 数字序列 [左偏树]

    题目传送门 数字序列 题目描述 给定一个整数序列 a1​,a2​,⋅⋅⋅,an​ ,求出一个递增序列 b1​<b2​<⋅⋅⋅<bn​ ,使得序列 ai​ 和 bi​ 的各项之差的绝对 ...

  2. 洛谷$P4331\ [BOI2004]\ Sequence$ 数字序列 左偏树

    正解:左偏树 解题报告: 传送门$QwQ$ 开始看到的时候$jio$得长得很像之前做的一个$dp$,,, 但是$dp$那题是说不严格这里是严格? 不难想到我们可以让$a_{i},b_{i}$同时减去$ ...

  3. Luogu P4331 [BOI2004]Sequence 数字序列 (左偏树论文题)

    清晰明了%%% Fairycastle的博客 个人习惯把size什么的存在左偏树结点内,这样在外面好写,在里面就是模板(只用修改update). 可以对比一下代码(好像也差不多-) MY CODE # ...

  4. [BOI2004]Sequence 数字序列

    Description: Hint: \(n<=10^5\) Solution: 首先考虑b不严格递增时的做法 发现当\(a[i]\)递增时\(b[i]\)直接取\(a[i]\)即可,否则此时需 ...

  5. luoguP4331 [BOI2004]Sequence 数字序列

    题意 大力猜结论. 首先将所有\(a_i\)变为\(a_i-i\),之后求不严格递增的\(b_i\),显然答案不变,最后\(b_i\)加上\(i\)即可. 考虑两种特殊情况: 1.\(a[]\)是递增 ...

  6. P4331 [BOI2004]Sequence 数字序列 (左偏树)

    [题目链接] https://www.luogu.org/problemnew/show/P4331 题目描述 给定一个整数序列\(a_1, a_2, ··· , a_n,\)求出一个递增序列\(b_ ...

  7. 洛谷P4331 [BOI2004]Sequence 数字序列(左偏树)

    传送门 感觉……不是很看得懂题解在说什么? 我们先把原数列$a_i-=i$,那么本来要求递增序列,现在只需要求一个非严格递增的就行了(可以看做最后每个$b_i+=i$,那么非严格递增会变为递增) 如果 ...

  8. 2021.08.01 P4311 数字序列(左偏树)

    2021.08.01 P4311 数字序列(左偏树) [P4331 BalticOI 2004]Sequence 数字序列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 重点: 1 ...

  9. 黄源河《左偏树的应用》——数字序列(Baltic 2004)

    这道题哪里都找不到. [问题描述] 给定一个整数序列a1, a2, … , an,求一个不下降序列b1 ≤ b2 ≤ … ≤ bn,使得数列{ai}和{bi}的各项之差的绝对值之和 |a1 - b1| ...

随机推荐

  1. git第一次提交代码到码云

    转载请标明出处:https://www.cnblogs.com/tangZH/p/10229598.html 不说废话,来看重点. 1.首先注册码云账号,然后建立仓库,这些就直接跳过,很简单. 2.下 ...

  2. java新知识系列 五

    类方法和对象方法的使用限制 abstract修饰符的注意 静态变量只能在类主体中定义,不能在方法中定义 线程的各种方法差别 关于抽象类 什么是中间件 Servlet生命周期的三个主要方法 可以修饰类的 ...

  3. 写入Log错误日志

    第一步创建ApplicationLog类 代码: using System;using System.Collections.Generic;using System.Linq;using Syste ...

  4. 完成代码将x插入到该顺序有序线性表中,要求该线性表依然有序

    #include <stdio.h> #include <malloc.h> int main(void) { int i, n; double s = 1.3; double ...

  5. LeetCode算法题-Base 7(Java实现)

    这是悦乐书的第247次更新,第260篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第114题(顺位题号是504).给定一个整数,返回其基数为7的字符串表示.例如: 输入: ...

  6. jdk 环境变量

    1. jdk安装后的目录 2.JAVA_HOME C:\Program Files\Java\jdk1.8.0_172 3.PATH %JAVA_HOME%\bin 4.CLASSPATH .;%JA ...

  7. Clion 配置

    plugins: one dark theme font : fira code retina

  8. windows做代理服务器让内部linux上网

    fiddler代理上网 1 下载安装:http://www.telerik.com/fiddl er 2 设置代理,如下图 3 代理服务器信息 代理服务器的IP : 10.1.44.11 代理服务器的 ...

  9. (转)lwip TCP client & FreeRTOS 打开TCP 的 保活机制 LWIP_TCP_KEEPALIVE==1

    参考大神教程:http://blog.sina.com.cn/s/blog_62a85b950101aw8x.html   老衲五木 :http://blog.sina.com.cn/s/blog_6 ...

  10. linux查询日志常用命令,经常更新

    1.grep命令 grep -c "查询内容" filename    ------c,是小写,可以知道你要查询的内容在这个文件中是否存在 grep -C 10 "查询内 ...