题面

Description

Input

Output

一个整数R

Sample Input

7
9
4
8
20
14
15
18

Sample Output

13

Hint

所求的Z序列为6,7,8,13,14,15,18.

R=13

Solution

我们首先来考虑另一个问题: 给定一个数列\(\{a_n\}\), 求一个单调不下降的\(\{b_n\}\), 使得\(\sum |b_n - a_n|\)最小.

考虑两种较为特殊情况:

  • \(a_1 \le a_2 \le ... \le a_n\), 此时\(b_n = a_n\)
  • \(a_1 \ge a_2 \ge ... \ge a_n\), 此时\(b_n = \{a_n\}的中位数\)

不难发现, 假如我们把\(\{a_n\}\)单调不下降的情况看作是一个数一段, 则它与\(\{a_n\}\)单调不上升的情况是等价的.

因此, 这道题目的做法就是: 从前往后分段, 对于\(a_n\)这一个数, 开始时我们把它单独作为一段, 假如这一段的中位数比上一段要小, 则把当前一段和上一段合并. 直至当前\(a_n\)所在段的中位数大于等于上一段的中位数或只剩下一段. 然后考虑数列上的下一个数.

回到原题, 由于原题要求\(z_n < z_{n + 1}\), 我们把读入的\(t_n\)变成\(b_n - n\), 再按照上述方法求解即可.

考虑如何动态维护中位数: 比较简单的想法就是启发式合并平衡树, 时间复杂度\(O(n \log^2 n)\), 会TLE. 下面是代码.

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cmath> namespace Zeonfai
{
inline int getInt()
{
int a = 0, sgn = 1;
char c; while(! isdigit(c = getchar()))
if(c == '-')
sgn *= -1; while(isdigit(c))
a = a * 10 + c - '0', c = getchar(); return a * sgn;
}
} const int N = (int)1e6;
int a[N], cnt; struct section
{
int L, R, w;
}sec[N]; struct splayTrees
{
struct node
{
int suc[2], pre, sz, w;
}nd[N]; int rt[N]; inline void newNode(int u, int w)
{
nd[u].w = w, nd[u].suc[0] = nd[u].suc[1] = nd[u].pre = -1, nd[u].sz = 1;
} inline void update(int u)
{
nd[u].sz = 1; for(int i = 0; i < 2; ++ i)
if(~ nd[u].suc[i])
nd[u].sz += nd[nd[u].suc[i]].sz;
} inline int getRelation(int u)
{
if(! (~ nd[u].pre))
return -1; return u == nd[nd[u].pre].suc[1];
} inline void rotate(int u)
{
int pre = nd[u].pre, prepre = nd[pre].pre, k = getRelation(u); if(~ nd[u].suc[k ^ 1])
nd[nd[u].suc[k ^ 1]].pre = pre; nd[pre].suc[k] = nd[u].suc[k ^ 1];
nd[u].suc[k ^ 1] = pre;
nd[u].pre = nd[pre].pre; if(~ prepre)
nd[prepre].suc[getRelation(pre)] = u; nd[pre].pre = u;
update(pre), update(u);
} inline void splay(int a, int u)
{
while(~ nd[u].pre)
{
int pre = nd[u].pre; if(~ nd[pre].pre)
rotate(getRelation(pre) == getRelation(u) ? pre : u); rotate(u);
} rt[a] = u;
} void _insert(int a, int u, int v)
{
++ nd[u].sz; if(nd[v].w < nd[u].w)
{
if(! (~ nd[u].suc[0]))
{
newNode(v, nd[v].w);
nd[v].pre = u;
nd[u].suc[0] = v;
splay(a, v);
}
else
_insert(a, nd[u].suc[0], v);
}
else
{
if(! (~ nd[u].suc[1]))
{
newNode(v, nd[v].w);
nd[v].pre = u;
nd[u].suc[1] = v;
splay(a, v);
}
else
_insert(a, nd[u].suc[1], v);
}
} inline void insert(int a, int u)
{
_insert(a, rt[a], u);
} void _merge(int a, int u)
{
int suc[2] = {nd[u].suc[0], nd[u].suc[1]};
insert(a, u); for(int i = 0; i < 2; ++ i)
if(~ suc[i])
_merge(a, suc[i]);
} inline int merge(int a, int b)
{
if(nd[rt[a]].sz < nd[rt[b]].sz)
std::swap(rt[a], rt[b]); _merge(a, rt[b]);
} int get(int u, int k)
{
if(~ nd[u].suc[0])
{
if(nd[nd[u].suc[0]].sz > k)
return get(nd[u].suc[0], k);
else
k -= nd[nd[u].suc[0]].sz;
} if(! k)
return nd[u].w; return get(nd[u].suc[1], k - 1);
} inline int getMedian(int a)
{
return get(rt[a], nd[rt[a]].sz >> 1);
}
}trees; int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ1367.in", "r", stdin);
freopen("BZOJ1367.out", "w", stdout);
#endif using namespace Zeonfai; int n = getInt(); for(int i = 0; i < n; ++ i)
trees.newNode(i, a[i] = getInt() - i); cnt = 0; for(int i = 0; i < n; ++ i)
{
trees.rt[cnt] = i;
sec[cnt].w = a[i], sec[cnt].L = i, sec[cnt ++].R = i; for(; cnt > 1 && sec[cnt - 2].w > sec[cnt - 1].w; -- cnt)
trees.merge(cnt - 2, cnt - 1), sec[cnt - 2].R = sec[cnt - 1].R, sec[cnt - 2].w = trees.getMedian(cnt - 2);
} long long ans = 0; for(int i = 0; i < cnt; ++ i)
for(int j = sec[i].L; j <= sec[i].R; ++ j)
ans += abs(a[j] - sec[i].w); printf("%lld", ans);
}

正解是左偏树维护中位数, \(O(n \log n)\)

因为我们合并的前提是:中位数(i)>中位数(i+1),那么对于合并后的i而言,中位数肯定是不升的

根据这个性质我们又可以用可并堆了,堆顶元素表示该序列中的中位数

当堆的元素个数*2>序列长度+1的时候就可以弹出堆顶

懒得写代码了.

最新文章

  1. if [ &quot;$变量1&quot;x = &quot;$变量2&quot;x ]中x的含义
  2. SQL--使用NewID函数,创建GUID列
  3. PostgreSQL 中定义自己需要的数据类型
  4. 【学习笔记】【C语言】循环结构-for
  5. 【AngularJs】---$sce 输出Html
  6. NSFileHandle 和 NSFileManager的一些用法
  7. LeetCode(3) || Median of Two Sorted Arrays
  8. sql-从查询结果创建一个永久表
  9. C#简单实现贪吃蛇程序(LinQ + Entity)
  10. [转]How to compile GDB for iOS!
  11. 如何使用XE2及更高版本中提供的自定义皮肤(样式)功能
  12. 201521123021《Java程序设计》第1周学习总结
  13. Solidity构造函数和析构函数
  14. graph_base_pic_segmentation里面的细节和代码
  15. springboot下整合各种配置文件
  16. 【开源GPS追踪】 之 硬件开源
  17. instruments symbol name 不显示函数名!
  18. Java SE 之 数据库操作工具类(DBUtil)设计
  19. Tomcat启动startup.bat闪退和JRE_HOME错误
  20. C++中 explicit的用法

热门文章

  1. PEP-8 规范1
  2. exp分析
  3. 数学基础:HUD1124-Factorial(N!末尾0的个数)
  4. Centos7重启网卡失败解决方法
  5. loj2028 「SHOI2016」随机序列
  6. 蓝桥杯Java输入输出相关
  7. C++异常安全的赋值运算符重载 【微软面试100题 第五十五题】
  8. day01_12.字符串
  9. [python工具][2]sublime的快捷键
  10. 紫书第三章训练1 E - DNA Consensus String