BZOJ 1367 [Baltic2004]sequence 可并堆

来源:互联网 发布:正版matlab软件价格 编辑:程序博客网 时间:2024/06/08 07:57

题意:链接

方法:可并堆

解析:

wzc讲的第二道可并堆?不这是第一道,然后之前他好像还讲了个双堆求中位数?

大概想想,是不是就是维护一个小根堆以及一个大根堆,之后每次来元素,比中位数大就加到小根堆,比中位数小就加到大根堆,之后如果两堆差超过了2,就往少的里加,之后元素多的堆里的堆顶元素是新中位数?

好像是吧我也没太听,不过自己YY这感觉像是对的?

反正我不会写堆


以上与本题无关


接下来说本题:

首先让我们这么想,如果一个递增序列,那么它的对应选取的序列就是其本身,对答案没有贡献,如果一个递减序列,那么答案是什么呢?

是中位数。

证明请出门左转找数学竞赛的同志。

所有的元素,我们可以看做将其分为若干个线段,每一个线段都有一个中位数,最终将所有的答案加到一起就是结果。

然后具体怎么处理呢?

首先有一个新的元素来了,我们把它看做一个新的堆,之后呢,我们要观察这个堆与前面的堆有没有什么关系,如果当前这个堆的中位数的值比前一个堆的中位数的值要小,那么就不满足递增性质了,所以我们需要将这两个堆合并。

然后就处理完了,最后统计答案。

另外,对于这些中位数,我们维护出来的可能不满足严格递增,有可能是含不下降的,然而这个怎么处理呢?

听说是将a[i]-i,好像是这样?

这样是把不下降转为递增?

这里我写的是大根堆,好像小根堆也可以?

但是总感觉讨论的时候复杂啊。

代码:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define N 1000100using namespace std;typedef long long ll;int ch[N][2],key[N],size[N],root[N],h[N],L[N],R[N],w[N],num[N],a[N];int n,m,x,tot,cnt;void pushup(int x){size[x]=size[ch[x][0]]+size[ch[x][1]]+1;}int merge(int x,int y){    if(!x)return y;    if(!y)return x;    if(key[x]<key[y])swap(x,y);    ch[x][1]=merge(ch[x][1],y);    pushup(x);    if(h[ch[x][0]]<h[ch[x][1]])swap(ch[x][0],ch[x][1]);    if(!h[ch[x][1]])h[x]=0;    else h[x]=h[ch[x][1]]+1;    return x;}int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i]-=i;    for(int i=1;i<=n;i++)    {        root[++tot]=++cnt;        key[cnt]=a[i],size[cnt]=1;        num[tot]=1,L[tot]=R[tot]=i;        while(tot>1&&key[root[tot]]<key[root[tot-1]])        {            tot--;            root[tot]=merge(root[tot],root[tot+1]);            R[tot]=R[tot+1];            num[tot]+=num[tot+1];            while(size[root[tot]]>(num[tot]+1)/2)            {                root[tot]=merge(ch[root[tot]][0],ch[root[tot]][1]);            }        }    }    ll ans=0;    for(int i=1;i<=tot;i++)    {        for(int j=L[i];j<=R[i];j++)        {            ans+=abs(key[root[i]]-a[j]);        }    }    printf("%lld\n",ans);}
0 0
原创粉丝点击