• BZOJ 1367 [Baltic2004]sequence (可并堆)


    题面:BZOJ传送门

    题目大意:给你一个序列$a$,让你构造一个递增序列$b$,使得$sum |a_{i}-b_{i}|$最小,$a_{i},b_{i}$均为整数

    神仙题..

    我们先考虑b不递减的情况

    假设现在有一段单调的序列$A$

    如果$A$是递增的,显然$b[i]=a[i]$是最优解

    如果$A$是递减的,$b$的每一项=序列$A$的中位数时是最优解

    简单证明一下递减的情况:

    1.序列$A$元素数量是奇数时,我们以中位数为对称轴,那么对称的两个数带来的贡献就是它们的差值,而中位数本身不会产生贡献,如果选取的不是中位数,必然会导致中位数产生贡献,而且对称的两个数还可能产生差值以外的贡献

    2.序列$A$元素数量是偶数时,选取的数在中间的两个数之间即可,贡献都是一样的

    我们如何利用这一性质呢?

    我们把序列拆分成很多递减的段,递增子段的每一个数都是单独的一段

    我们的目的是保证$b$不递减,即把每一段取的数画成一个函数来看是不递减的

    每次我们在序列末尾加入一个数$a_{i}$,都看看这一段的中位数是否$geq $前面一段的中位数,不满足就和前一段合并,然后依次重复此过程

    为什么答案合并后不会变差?太弱了并不会证..感性理解一下吧。因为这两段原来取的就是最优解,但并不满足要求,我们也只能把两段合并,再用相同的方法求最优解了..

    具体实现可以用左偏树维护大根堆,记录每一段较大的$left lceil frac{n}{2} ight ceil$个数,当两个奇数段合并时删除一次堆顶即可,记录每一段的信息可以用结构体

     1 #include <cmath>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #define N1 1000010
     6 #define ll long long 
     7 using namespace std;
     8 const ll inf=0x3f3f3f3f3f3f3f3fll;
     9  
    10 template <typename _T> void read(_T &ret)
    11 {
    12     ret=0; _T fh=1; char c=getchar();
    13     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
    14     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
    15     ret=ret*fh;
    16 }
    17  
    18 struct Heap{
    19 int fa[N1],ch[N1][2],h[N1]; ll val[N1];
    20 int merge(int x,int y)
    21 {
    22     if(!x||!y) return x+y;
    23     if(val[x]<val[y]) swap(x,y);
    24     //pushdown(x);
    25     ch[x][1]=merge(ch[x][1],y); fa[ch[x][1]]=x;
    26     if(h[ch[x][0]]<h[ch[x][1]]) swap(ch[x][0],ch[x][1]);
    27     h[x]=h[ch[x][1]]+1;
    28     return x;
    29 }
    30 int del(int x)
    31 {
    32     fa[ch[x][0]]=fa[ch[x][1]]=0; 
    33     int y=merge(ch[x][0],ch[x][1]);
    34     ch[x][0]=ch[x][1]=0;
    35     return y;
    36 }
    37 }h;
    38  
    39 ll a[N1];
    40 struct node{int l,r,x;};
    41 node stk[N1]; int n,tp;
    42  
    43 int main()
    44 {
    45     scanf("%d",&n);
    46     int i,j,x,y,len; node K; ll ans=0,tmp;
    47     for(i=1;i<=n;i++) read(a[i]);
    48     for(i=1;i<=n;i++)
    49     {
    50         h.val[i]=a[i]-i; x=i; len=1;
    51         while(tp)
    52         {
    53             K=stk[tp]; 
    54             if(h.val[x]>=h.val[K.x]) break;
    55             x=h.merge(x,K.x); tp--;
    56             if( (len&1) && ((K.r-K.l+1)&1) ) x=h.del(x);
    57             len+=K.r-K.l+1;
    58         }
    59         stk[++tp]=(node){i-len+1,i,x};
    60     }
    61     for(i=1;i<=tp;i++)
    62     for(j=stk[i].l;j<=stk[i].r;j++)
    63     {
    64         tmp=a[j]-(h.val[stk[i].x]+j);
    65         ans+=((tmp>0)?tmp:-tmp);
    66     }
    67     printf("%lld
    ",ans);
    68     return 0;
    69 }
  • 相关阅读:
    关于排序算法的记录
    java获取src下文件
    学习HashMap的笔记
    红黑树删除
    学习红黑树过程中的个人总结
    关于二叉树的记录
    关于自动装箱和自动拆箱
    学习函数的时候问题
    Oracle 实现拆分列数据的split()方法
    福大软工 · 最终作业
  • 原文地址:https://www.cnblogs.com/guapisolo/p/10434575.html
Copyright © 2020-2023  润新知