zoukankan      html  css  js  c++  java
  • 【BZOJ 1367】 1367: [Baltic2004]sequence (可并堆-左偏树)

    1367: [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

    Source

    【分析】

      这题主要是要证明结论。详见hyh的论文。

      先说说结论做法:

      把序列分成m个区间,每个区间最后到达的值都是u。u为这个区间所有数的中位数。

      先做一个小小的转化,题目要求b1<b2<...b3,可以变成b1-1<=b2-2<=b3-3<=...bi-i...【经典的标号的加减,加上一个等号

      然后是这样做:

    假设我们已经找到前 k 个数 a[1], a[2], … , a[k] (k<n) 的最优解,得到 m 个区间组成的队列,对应的解为 (w[1],w[2],…,w[m]),现 在要加入 a[k+1],并求出前 k+1 个数的最优解。首先我们把 a[k+1] 作为一个新区 间直接加入队尾,令 w[m+1]=a[k+1],然后不断检查队尾两个区间的解 w[m] 和
    w[m+1],如果 w[m] >w[m+1],我们需要将最后两个区间合并,并找出新区间的 最优解(也就是序列 a 中,下标在这个新区间内的各项的中位数)。重复这个合 并过程,直至 w[1] ≤ w[2] ≤ … ≤ w[m] 时结束,然后继续处理下一个数。

      正确性证明:

      若某序列前半部分 a[1], a[2], … , a[n] 的最优解为 (u,u,…,u),后半部分a[n+1], a[n+2], ... , a[m] 的最优解为 (v,v,…,v),那么整个序列的最优解是什么 呢?

      若 u≤ v,显然整个序列的最优解为 (u,u,…,u,v,v,…,v) 。

      否则,设整个序列的最优解为 ( b[1],b[2],…,b[m] ),则显然 b[n] ≤ u(否则我们把前半部分的解( b[1],b[2], …,b[n]) 改为 (u,u,…,u),由题设知整个序列的解不会变坏),同理b[n+1] ≥ v。

      接下来,我们将看到下面这个事实: 对于任意一个序列 a[1] ,a[2],…,a[n],如果最优解为 (u,u,…,u),那么在满足u≤ u′≤ b[1] 或 b[n] ≤ u′≤ u 的情况下, (b[1],b[2],…,b[n]) 不会比 ( u′ ,u′ ,…,u′ ) 更优。

      我们用归纳法证明 u≤ u′≤ b[1] 的情况, b[n] ≤ u′≤ u 的情况可以类似证明。

      当 n=1 时, u=a[1],命题显然成立。 当n>1 时,假设对于任意长度小于n的序列命题都成立,现在证明对于长度 为n的序列命题也成立。

      首先把 (b[1], b[2], … b[n]) 改为 (b[1], b[1], … b[1]),这 一改动将不会导致解变坏,因为如果解变坏了,由归纳假设可知a[2],a[3],…,a[n] 的中位数w>u,这样的话,最优解就应该为(u,u,…,u,w,w,…,w),矛盾。

      然后我们再把(b[1],b[1],…,b[1])改为 ( u′ ,u′ ,…,u′ ),由于 | a[1] - x | + | a[2] - x | + …+ | a[n] - x | 的几何意义为数轴上点x到点a[1], a[2], … a[n] 的距离之和,且u≤ u′≤ b[1],显然点u′ 到各点的距离之和不会比点b[1] 到各点的距离之和大,也 就是说, (b[1],b[1],…,b[n]) 不会比 (v,v,…,v) 更优。(证毕)

      再回到之前的论述,由于 b[n] ≤ u,作为上述事实的结论,我们可以得知, 将 ( b[1],b[2],…,b[n] ) 改为 (b[n],b[n],…,b[n]),再将 ( b[n+1],b[n+2],…,b[m]) 改为 ( b[n+1],b[n+1], …,b[n+1]),并不会使解变坏。

    也就是说,整个序列的最优 解为 ( b[n],b[n],…,b[n],b[n+1],b[n+1],…,b[n+1])。再考虑一下该解的几何意 义,设整个序列的中位数为 w,则显然令 b[n]=b[n+1]=w 将得到整个序列的最优 解,即最优解为 (w,w,…,w)。

      (以上证明来自论文ORZ)

    代码:

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 #include<algorithm>
     6 using namespace std;
     7 #define Maxn 1000010
     8 #define LL long long
     9 
    10 int a[Maxn];
    11 
    12 int myabs(int x) {return x<0?-x:x;}
    13 
    14 struct node
    15 {
    16     int v,siz,dis,lc,rc;
    17 }t[Maxn];
    18 
    19 struct Ltree
    20 {
    21     int cnt;
    22     int merge(int x,int y)
    23     {
    24         if(x==0||y==0) return x+y;
    25         if(t[x].v<t[y].v) swap(x,y);
    26         t[x].rc=merge(t[x].rc,y);
    27         t[x].siz=t[t[x].lc].siz+t[t[x].rc].siz+1;
    28         if(t[t[x].rc].dis>t[t[x].lc].dis) swap(t[x].lc,t[x].rc);
    29         t[x].dis=t[t[x].rc].dis+1;
    30         return x;
    31     }
    32     inline int top(int x) {return t[x].v;}
    33     inline int size(int x) {return t[x].siz;}
    34     inline void pop(int &x) {x=merge(t[x].lc,t[x].rc);}
    35     inline int add(int x)
    36     {
    37         t[++cnt].v=x;t[cnt].siz=1;
    38         t[cnt].lc=t[cnt].rc=t[cnt].dis=0;
    39         return cnt;
    40     }
    41 }heap;
    42 
    43 int rt[Maxn],tot[Maxn];//区间
    44 int l[Maxn],r[Maxn];
    45 
    46 int main()
    47 {
    48     int n;
    49     scanf("%d",&n);
    50     for(int i=1;i<=n;i++) {scanf("%d",&a[i]);a[i]-=i;}
    51     int now=0;
    52     for(int i=1;i<=n;i++)
    53     {
    54         now++;
    55         rt[now]=heap.add(a[i]);
    56         l[now]=r[now]=i;tot[now]=1;
    57         while(now>1&&heap.top(rt[now-1])>heap.top(rt[now]))
    58         {
    59             now--;
    60             rt[now]=heap.merge(rt[now],rt[now+1]);
    61             tot[now]+=tot[now+1];r[now]=r[now+1];
    62             while(heap.size(rt[now])*2>tot[now]+1)
    63                 heap.pop(rt[now]);
    64         }
    65     }
    66     LL ans=0;
    67     for(int i=1;i<=now;i++)
    68     {
    69         int xx=heap.top(rt[i]);
    70         for(int j=l[i];j<=r[i];j++)
    71         {
    72           ans+=myabs(a[j]-xx);
    73         }
    74     }
    75     printf("%lld
    ",ans);
    76     return 0;
    77 }
    View Code

    2017-01-16 15:30:21


    左偏树:(上面的这份代码的左偏树还少了O(n)构建的部分。)

    概念:

      优先队列 

      优先队列(Priority Queue)是一种抽象数据类型(ADT),它是一种容器,里面 有一些元素,这些元素也称为队列中的节点(node)。优先队列的节点至少要包含 一种性质:有序性,也就是说任意两个节点可以比较大小。为了具体起见我们假 设这些节点中都包含一个键值(key),节点的大小通过比较它们的键值而定。优 先队列有三个基本的操作:插入节点(Insert),取得最小节点(Minimum) 和删除 最小节点(Delete-Min)。

      可并堆

      可并堆(Mergeable Heap)也是一种抽象数据类型,它除了支持优先队列的三 个基本操作(Insert, Minimum, Delete-Min), 还支持一个额外的操作——合并操作。

      左偏树

      左偏树(Leftist Tree)是一种可并堆的实现。左偏树是一棵二叉树,它的节点 除了和二叉树的节点一样具有左右子树指针( left, right ) 外,还有两个属性:键值 和距离(dist)。

      1、外节点:节点 i 称为外节点(external node),当且仅当节点 i 的左子树或右子树为空 ( left(i) = NULL 或 right(i) = NULL );
      2、距离:点 i 的距离( dist( i ) ) 是节点 i 到它的后代 中,最近的外节点所经过的边数。特别的,如果节点 i 本身是外节点,则它的距 离为 0;而空节点的距离规定为-1 (dist(NULL) = -1)。在本文中,有时也提到一 棵左偏树的距离,这指的是该树根节点的距离。




    左偏树的性质

    [性质 1] 节点的键值小于或等于它的左右子节点的键值。
    [性质 2] 节点的左子节点的距离不小于右子节点的距离。
    [性质 3] 节点的距离等于它的右子节点的距离加 1
    [引理 1] 若左偏树的距离为一定值, 则节点数最少的左偏树是完全二叉树。
    [定理 1] 若一棵左偏树的距离为k,则这棵左偏树至少有 2k+1-1 个节点。
    [性质 4] 一棵 N 个节点的左偏树距离最多为 [log(N+1)]-1。

    性质4决定了左偏树的时间复杂度是较低的。

    左偏树的合并:

    int merge(int x,int y)
    {
    	if(x==0||y==0) return x+y;
    	if(t[x].v<t[y].v) swap(x,y);
    	t[x].rc=merge(t[x].rc,y);
    	t[x].siz=t[t[x].lc].siz+t[t[x].rc].siz+1;
    	if(t[t[x].rc].dis>t[t[x].lc].dis) swap(t[x].lc,t[x].rc);
    	t[x].dis=t[t[x].rc].dis+1;
    	return x;
    }
    

    删除:

    inline void pop(int &x) {x=merge(t[x].lc,t[x].rc);}
    

    添加一个节点形成一颗新的左偏树:

    inline int add(int x)
    {
    	t[++cnt].v=x;t[cnt].siz=1;
    	t[cnt].lc=t[cnt].rc=t[cnt].dis=0;
    	return cnt;
    }
    

    各种堆的比较:

    左偏树真是真短真美丽233

    2017-01-16 16:08:10

  • 相关阅读:
    SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
    HAService 刨坑
    RocketMQ服务器监控误区
    Send [1] times, still failed
    RECONSUME_LATER
    RocketMQ 自定义文件路径
    RocketMQ 运维指令
    Thrift 学习记录
    服务网格(Service Mesh)学习记录
    Linux 安装 Apache
  • 原文地址:https://www.cnblogs.com/Konjakmoyu/p/6289803.html
Copyright © 2011-2022 走看看