bzoj1367 [Baltic2004]sequence [左偏树]

新博客宣传一发http://shijieyywd.com/?p=73

Description

给定一个序列t1,t2,...,tn,求一个递增序列z1<z2<...<zn, 使得R=|t1?z1|+|t2?z2|+...+|tn?zn|的值最小。本题中,我们只需要求出这个最小的R值。

Input

第1行为一个整数n(1<=n<=106), 第2行到第n + 1行,每行一个整数,第k + 1行为tk(0<=tk<=2?109).

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

论文:左偏树的特点及其应用

现学了一波左偏树orz.

先考虑只要求z1<=z2<=...<=zn:

两个特殊情况:

-t[1]<=t[2]<=...<=t[n],此时z[i] = t[i].

-t[1]>=t[2]>=...>=t[n],此时z[i]=x,x为序列t的中位数.

于是可以将原数列划分成m个区间,每一段的解为该区间的中位数。

实现:

假设已经求出了前k个数的最优解,被划分成了m个区间,每段区间的最优解为w\[i\](w[1]<=w[2]<=...<=w[m]),现在考虑第k + 1个数,先将t[k + 1]单独看作一个区间,最优解为w[m+1],此时假如w[m]>w[m+1],则合并区间m,m + 1,然后找出新区间的解(中位数),重复上述过程直到w[m]<=w[m+1].

如何维护中位数:当堆的大小大于区间长度的一半时删除堆顶元素,则堆中的元素一定是该区间内较小的一半元素,堆顶元素即为该区间的中位数。

这只是z1<=z2<=...<=zn的情况。。

然而要求递增只需要将原本的t[i]改成t[i] - i,再按照上述做法做就行了orz。

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 1000005;

int n;
int arr[MAXN];
int tot = 0;
int v[MAXN], tr[MAXN][2], dist[MAXN], siz[MAXN];
int root[MAXN];
int l[MAXN], r[MAXN];

int merge(int x, int y) {
    if (!x || !y) return x + y;
    if (v[x] < v[y]) swap(x, y);
    tr[x][1] = merge(tr[x][1], y);
    siz[x] = siz[tr[x][0]] + siz[tr[x][1]] + 1;
    if (dist[tr[x][1]] > dist[tr[x][0]]) swap(tr[x][0], tr[x][1]);
    dist[x] = dist[tr[x][1]] + 1;
    return x;
}

int top(int x) {
    return v[x];
}

int size(int x) {
    return siz[x];
}

void pop(int &x) {
    x = merge(tr[x][0], tr[x][1]);
}

int newNode(int x) {
    v[++tot] = x;
    siz[tot] = 1;
    tr[tot][0] = tr[tot][1] = dist[tot] = 0;
    return tot;
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &arr[i]);
        arr[i] -= i;
    }
    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        cnt++;
        root[cnt] = newNode(arr[i]);
        l[cnt] = r[cnt] = i;
        while (cnt > 1 && top(root[cnt]) < top(root[cnt - 1])) {
            cnt--;
            root[cnt] = merge(root[cnt], root[cnt + 1]);
            r[cnt] = r[cnt + 1];
            while (size(root[cnt]) * 2 > r[cnt] - l[cnt] + 2)
                pop(root[cnt]);
        }
    }
    long long ans = 0;
    for (int i = 1; i <= cnt; i++) {
        int t = top(root[i]);
        for (int j = l[i]; j <= r[i]; j++) {
            ans += abs(t - arr[j]);
        }
    }
    printf("%lld\n", ans);
    return 0;
}
时间: 2024-10-22 13:00:01

bzoj1367 [Baltic2004]sequence [左偏树]的相关文章

【BZOJ1367】[Baltic2004]sequence 左偏树

[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 题解:详见论文 然而本题要求z[i]严格递增,那就让所有t[i]-=i就好了 #include <cstdio> #include <cstring> #include <iostrea

【左偏树+贪心】BZOJ1367-[Baltic2004]sequence

[题目大意] 给定一个序列t1,t2,...,tn ,求一个递增序列z1<z2<...<zn , 使得R=|t1−z1|+|t2−z2|+...+|tn−zn| 的值最小.本题中,我们只需要求出这个最小的R值. [思路] -这个比加延迟标记的左偏树调试得还久……WA到死…… 如果ti是递增的,我们只需要取zi=ti: 如果ti是递减的,我们只需要取ti的中位数. 所以我们将ti分割成若干个区间,维护每个区间的中位数.对于[L,R]的区间,我们存放[L,(L+R)/2]在堆中.具体如下操作

浅谈左偏树在OI中的应用

Preface 可并堆,一个听起来很NB的数据结构,实际上比一般的堆就多了一个合并的操作. 考虑一般的堆合并时,当我们合并时只能暴力把一个堆里的元素一个一个插入另一个堆里,这样复杂度将达到\(\log(|A|)+\log(|B|)\),极限数据下显然是要T爆的. 所以我们考虑使用一种性价比最高的可并堆--左偏树,它的思想以及代码都挺简单而且效率也不错. 学习和参考自这里 What is Leftist Tree 左偏树,顾名思义就是像左偏的树,但是这样抽象的表述肯定是不符合我们学OI的人的背板子

POJ3666-Making the Grade(左偏树 or DP)

左偏树 炒鸡棒的论文<左偏树的特点及其应用> 虽然题目要求比论文多了一个条件,但是……只需要求非递减就可以AC……数据好弱…… 虽然还没想明白为什么,但是应该觉得应该是这样——求非递减用大顶堆,非递增小顶堆…… 这题和bzoj1367题意差不多,但是那题求的是严格递增.(bzoj找不到那道题,可能是VIP或什么原因? 严格递增的方法就是每一个数字a[i]都要减去i,这样求得的b[i]也要再加i,保证了严格递增(为什么对我就不知道了 代码比较水,因为题目数据的问题,我的代码也就钻了空子,反正ac

关于左偏树的一些东东

大概所有的预备知识这里都有https://baike.baidu.com/item/%E5%B7%A6%E5%81%8F%E6%A0%91/2181887?fr=aladdin 例题1:洛谷 P3377 [模板]左偏树(可并堆) 383通过 1.2K提交 题目提供者HansBug 站长团 标签 难度提高+/省选- 时空限制1s / 128MB 提交 讨论 题解 最新讨论更多讨论 加了路径压缩就WA,路过dal… 左偏树用指针写会MLE吗..… m,n写反了也可以过,数据有… 哪位大神有pbds库

左偏树

概要:左偏树是具有左偏性质的堆有序二叉树,它相比于优先队列,能够实现合并堆的功能. 先仪式型orzorzozr国家集训队论文https://wenku.baidu.com/view/515f76e90975f46527d3e1d5.html 左偏树的节点定义: 1 struct node { 2 int lc, rc, val, dis; 3 } LTree[maxn]; 左偏树的几个基本性质如下: 节点的键值小于等于它的左右子节点的键值 节点的左子节点的距离不小于右子节点的距离 节点的距离等于

学习笔记——左偏树

左偏树是一个堆,为了实现快速合并的操作,我们可以构造一颗二叉树,并且使右子树尽量简短 什么是左偏呢? 定义:一个左偏树的外节点是一个左子树为空或者右子树为空的节点,对于每一个点定义一个距离dist它为到它子树内外节点的最短距离. 一个合法的左偏树节点需要满足堆性以及它的右子树的dist比左子树的dist小. 为什么要这样呢? 这样右子树的dist是严格控制在logn以内的. 于是我们合并的时候,将另一个左偏树与当前左偏树的右子树合并,这样递归下去,则时间复杂度是O(logn)的. 这就是一颗左偏

poj 3016 K-Monotonic 左偏树 + 贪心 + dp

//poj 3016 K-Monotonic//分析:与2005年集训队论文黄源河提到的题目类似,给定序列a,求一序列b,b不减,且sigma(abs(ai-bi))最小.//思路:去除左偏树(大根堆)一半的节点(向上取整),让左偏树的根节点上存放中位数:每个左偏树的根节点表示一个等值区间//在本题中,我们将一段区间 与 一颗左偏树等同:将求调整给定数列 vi 为不减序列的代价 与 求取数列 bi 等同 1 #include"iostream" 2 #include"cstd

BZOJ 1455 罗马游戏 左偏树

题目大意:给定n个点,每个点有一个权值,提供两种操作: 1.将两个点所在集合合并 2.将一个点所在集合的最小的点删除并输出权值 很裸的可并堆 n<=100W 启发式合并不用想了 左偏树就是快啊~ #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 1001001 using namespace std; struct abcd{ abcd