您的位置:首页 > 产品设计 > UI/UE

BZOJ 1367 [Baltic2004]sequence 可并堆

2015-07-23 19:08 531 查看

题意:链接

方法:可并堆

解析:

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

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

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

反正我不会写堆

以上与本题无关

接下来说本题:

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

是中位数。

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

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

然后具体怎么处理呢?

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

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

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

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

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

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

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

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1000100
using namespace std;
typedef long long ll;
int ch
[2],key
,size
,root
,h
,L
,R
,w
,num
,a
;
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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: