BZOJ_1367_[Baltic2004]sequence_结论题+可并堆

BZOJ_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


 

神题。有一个结论:最优解一定是把序列分成m块,每块取中位数,并使得取出的序列递增。

不会证?https://wenku.baidu.com/view/20e9ff18964bcf84b9d57ba1.html

然后发现后面的合法中位数递增,搞来一个可并大根堆来维护,当堆内元素超过序列的一半时弹出。

然而结论要求序列不降,这里求序列递增。

我们发现每个递增的序列的每个数$ai$ 减去$i$ 对应着唯一一个不降序列,故在一开始对序列进行处理。

 

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
char nc() {
    static char buf[100000],*p1,*p2;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd() {
    register int x=0; register char s=nc();
    while(s<'0'||s>'9') s=nc();
    while(s>='0'&&s<='9') x=x*10+s-'0',s=nc();
    return x;
}
int fabs(int x){return x>0?x:-x;}
#define N 1000050
typedef long long ll;
int n,a[N],root[N],siz[N],val[N],ls[N],rs[N],dis[N],lp[N],rp[N];
ll ans;
int merge(int x,int y) {
    if(!x) return y;
    if(!y) return x;
    if(val[x]<val[y]) swap(x,y);
    siz[x]+=siz[y];
    rs[x]=merge(rs[x],y);
    if(dis[rs[x]]>dis[ls[x]]) swap(ls[x],rs[x]);
    dis[x]=dis[rs[x]]+1;
    return x;
}
int main() {
    n=rd(); 
    int i,tot=0,m=0,j;
    for(i=1;i<=n;i++) a[i]=rd(),a[i]-=i;
    dis[0]=-1;
    for(i=1;i<=n;i++) {
        root[++m]=++tot; val[tot]=a[i]; siz[tot]=1; lp[m]=rp[m]=i;
        while(m>1&&val[root[m]]<val[root[m-1]]) {
            m--; root[m]=merge(root[m],root[m+1]); rp[m]=rp[m+1];
            while(2*siz[root[m]]>rp[m]-lp[m]+2) root[m]=merge(ls[root[m]],rs[root[m]]);
        }
    }
    for(i=1;i<=m;i++) {
        for(j=lp[i];j<=rp[i];j++) {
            ans+=fabs(val[root[i]]-a[j]);
        }
    }
    printf("%lld\n",ans);
}

 

posted @ 2018-04-20 16:21  fcwww  阅读(156)  评论(0编辑  收藏  举报