BZOJ1367【Baltic2004】sequence
题面
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的时候就可以弹出堆顶
懒得写代码了.
BZOJ1367【Baltic2004】sequence的更多相关文章
- 【BZOJ1367】【Baltic2004】sequence - 可合并堆
题意: 题解: 其实这是道水题啦……只不过我没做过而已 先考虑构造不严格递增序列,考虑原序列中的一段下降区间,显然区间中的$z$全取中位数最优: 那么可以把原序列拆成很多个下降序列,从头到尾加入原序列 ...
- 【XSY2985】【BZOJ1367】【Baltic2004】sequence
考虑两种情况: 1.\(a_1\)<\(a_2\)<\(a_3\)<\(a_4\)...<\(a_n\) 直接令\(b_i\)=\(a_i\),最小. 2.\(a_1\)> ...
- 【dfs】Sequence Decoding
Sequence Decoding 题目描述 The amino acids in proteins are classified into two types of elements, hydrop ...
- 【XSY2564】sequence
Description [题目描述] 给定一个长度为n的由['0'..'9']组成的字符串s,v[i,j]表示由字符串s第i到第j位组成的十进制数字. 将它的某一个上升序列定义为:将这个字符串切割成m ...
- 【测试题】sequence
题目 给定一个长度为n(n<=5000)的由['0'..'9']组成的字符串s,v[i,j]表示由字符串s第i到第j位组成的十进制数字. 将它的某一个上升序列定义为:将这个字符串切割成m段不含前 ...
- 【LeetCode】图论 graph(共20题)
[133]Clone Graph (2019年3月9日,复习) 给定一个图,返回它的深拷贝. 题解:dfs 或者 bfs 都可以 /* // Definition for a Node. class ...
- 【LeetCode】拓扑排序 topological-sort(共5题)
[207]Course Schedule [210]Course Schedule II [269]Alien Dictionary [329]Longest Increasing Path in a ...
- 【LeetCode】拓扑排序
[207] Course Schedule 排课问题,n门课排课,有的课程必须在另外一些课程之前上,问能不能排出来顺序. 题解:裸的拓扑排序.参考代码见算法竞赛入门指南这本书. class Solut ...
- 论文阅读(Xiang Bai——【PAMI2017】An End-to-End Trainable Neural Network for Image-based Sequence Recognition and Its Application to Scene Text Recognition)
白翔的CRNN论文阅读 1. 论文题目 Xiang Bai--[PAMI2017]An End-to-End Trainable Neural Network for Image-based Seq ...
随机推荐
- LeetCode(134) Gas Station
题目 There are N gas stations along a circular route, where the amount of gas at station i is gas[i]. ...
- uncaught exception 'NSInternalInconsistencyException, reason:[UITableViewController loadView] loaded the "Controller" nib but didn't get a UITableView
http://blog.csdn.net/ryantang03/article/details/7941058#reply 上面那篇文章是我查找的ios实现下拉刷新功能,在我下载完代码运行的过程中发现 ...
- 2018 Multi-University Training Contest 10 CSGO(HDU - 6435)(最远曼哈顿距离)
有 n 种主武器,m 种副武器.每种武器有一个基础分数k种属性值 X[i] . 选出一种主武器 mw 和一种副武器 sw,使得两种武器的分数和 + 每个属性的差值尽量大.(参考下面的式子) 多维的最远 ...
- Android CTS - Cannot run program "aapt"/ Fail to run aapt on .../apk installed but AaptParser failed
今天同事碰到cts的一些问题,跑到某个apk的时候,就提示如下错误: Cannot run program "aapt": error=2. No such file or dir ...
- 算法学习记录-查找——二叉排序树(Binary Sort Tree)
二叉排序树 也称为 二叉查找数. 它具有以下性质: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值. 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值. 它的左.右子树也分别 ...
- HDU 4605 Magic Ball Game 主席树
题意: 给一棵\(n(1 \leq n \leq 10^5)\)个节点的二叉树,除叶子节点外,每个点都有左儿子和右儿子. 每个点上都有一个权值. 游戏规则是这样的:在根节点放一个权值为\(X\)的小球 ...
- python基础学习笔记——循环语句(while、for)
while 循环 流程控制语句 while 1.基本循环 while 条件: # 循环体 # 如果条件为真,那么循环则执行 # 如果条件为假,那么循环不执行 2.break break 用于退出当 ...
- hdu3613 Best Reward
先manacher.然后前缀和价值,枚举切点,O(1)判断切后是否回文 #include <iostream> #include <cstring> #include < ...
- 25、Base64
Base64要求把每三个8Bit的字节转换为四个6Bit的字节(3*8 = 4*6 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3 ...
- Leetcode24--->Swap Nodes in Pairs(交换单链表中相邻的两个节点)
题目:给定一个单链表,交换两个相邻的节点,且返回交换之后的头节点 举例: Given 1->2->3->4, you should return the list as 2-> ...