• [BOI2004]Sequence 数字序列


    嘟嘟嘟

     

    这道题要是直接想正解实在太难了,还得从一些特殊的情况一点点入手。

    1.如果ai本身就是递增的,那么令bi = ai即最优解。

    2.如果ai严格递减,则b1 = b2 = b3 = ……= bn = 中位数为最优解。这个可以用初中的几何证明:把 |bi - ai| 想象成数轴上两点间距离,那么 |x - a1| + |x - a2|的最优解x一定a1和a2之间;扩展到三项:|x - a1| + |x - a2| + |x - a3|,那么x就是中位数;以此类推,x是中位数得证。

    现在再想想一般情况:对于数列{an},可能有一段递增,有一段递减,那么就把这些看成一段一段的:初始每一段的长度都为1,令中位数为ci,则ci = ai。若ci < ci+1,那么就保持不变;否则将ci和ci+1所在的区间合并,取一个新的中位数,作为新区间的答案。就这样不断的合并,直到ci不下降。

    因此我们需要一个数据结构,支持合并、查询最大值和删除。为什么要查询最大值和删除呢?因为维护中位数可以只维护⌈1/2区间长度⌉小的数,用一个大根堆,则堆顶就是中位数。合并完两个区间后,就一直删除堆顶,直到元素个数 = ⌈1/2区间长度⌉。

    那么就能想到用左偏树实现啦。所以左偏树并不是这道题的核心,实际上只起到了一个优化的作用。

    最后一点,就是题中让求的是严格递增,然而我们一直是在不下降的前提下思考的。有一个巧妙的做法,就是开始ai -= i,就转换成了不下降。

    代码稍微参考了别人的题解(左偏树还是不太熟啊)。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<cstring>
     6 #include<cstdlib>
     7 #include<cctype>
     8 #include<vector>
     9 #include<stack>
    10 #include<queue>
    11 using namespace std;
    12 #define enter puts("") 
    13 #define space putchar(' ')
    14 #define Mem(a, x) memset(a, x, sizeof(a))
    15 #define rg register
    16 typedef long long ll;
    17 typedef double db;
    18 const int INF = 0x3f3f3f3f;
    19 const db eps = 1e-8;
    20 const int maxn = 1e6 + 5;
    21 inline ll read()
    22 {
    23   ll ans = 0;
    24   char ch = getchar(), last = ' ';
    25   while(!isdigit(ch)) {last = ch; ch = getchar();}
    26   while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
    27   if(last == '-') ans = -ans;
    28   return ans;
    29 }
    30 inline void write(ll x)
    31 {
    32   if(x < 0) x = -x, putchar('-');
    33   if(x >= 10) write(x / 10);
    34   putchar(x % 10 + '0');
    35 }
    36 
    37 int n;
    38 
    39 struct Node
    40 {
    41   int root, l, r, siz;
    42   ll val;
    43 }st[maxn];
    44 int top = 0;
    45 
    46 ll val[maxn];
    47 int dis[maxn], ls[maxn], rs[maxn];
    48 int merge(int x, int y)
    49 {
    50   if(!x || !y) return x | y;
    51   if(val[x] < val[y]) swap(x, y);
    52   rs[x] = merge(rs[x], y);
    53   if(dis[rs[x]] > dis[ls[x]]) swap(rs[x], ls[x]);
    54   dis[x] = dis[rs[x]] + 1;
    55   return x;
    56 }
    57 int Del(int x)
    58 {
    59   return merge(ls[x], rs[x]);
    60 }
    61 
    62 ll ans = 0;
    63 
    64 int main()
    65 {
    66   n =  read();
    67   for(int i = 1; i <= n; ++i) val[i] = read() - i;
    68   st[++top] = (Node){1, 1, 1, 1, val[1]};
    69   for(int i = 2; i <= n; ++i)
    70     {
    71       st[++top] = (Node){i, i, i, 1, val[i]};
    72       while(top && st[top].val < st[top - 1].val)
    73     {
    74       top--;
    75       st[top].root = merge(st[top].root, st[top + 1].root);
    76       st[top].siz += st[top + 1].siz;
    77       st[top].r = st[top + 1].r;
    78       while(st[top].siz > (st[top].r - st[top].l + 1 + 1) >> 1)    //向上取整 
    79         {
    80           st[top].siz--;
    81           st[top].root = Del(st[top].root);
    82         }
    83       st[top].val = val[st[top].root];
    84     }
    85     }
    86   for(int i = 1, j = 1; i <= n; ++i)
    87     {
    88       if(i > st[j].r) j++;
    89       ans += abs(st[j].val - val[i]);
    90     }
    91   write(ans); enter;
    92   for(int i = 1, j = 1; i <= n; ++i)
    93     {
    94       if(i > st[j].r) j++;
    95       write(st[j].val + i), space;
    96     }
    97   enter;
    98   return 0;
    99 }
    View Code
  • 相关阅读:
    浅析如何给Linux中增加磁盘并进行系统盘数据迁移:创建分区、格式化分区、数据迁移、挂载分区
    Linux基础:磁盘管理(df、du、fdisk)、磁盘挂载与卸除、数据盘与系统盘的查看
    Linux基础:文件基础属性及如何更改文件属性、文件与目录管理、linux软硬链接的理解、linux用户和用户组管理
    浅析如何选择picture标签和img标签、如何使用 srcset 和 sizes 属性进行分辨率切换、如何使用picture标签的source和media在不同设备显示不同图像
    浅析flex布局被子元素内容撑破的问题
    SQL优化:慎用标量子查询,改用left join提升查询效率
    【Centos】查询命令
    【RocketMQ】读写队列
    【Centos】命令自定义
    关于 JavaScript 中的 Array.reduce()
  • 原文地址:https://www.cnblogs.com/mrclr/p/9800470.html
Copyright © 2020-2023  润新知