bzoj1367: [Baltic2004]sequence

首先先减下标转换成求最长不下降子序列(是个套路?见了几次还是不是很懂)

然后对于递增的直接取值就可以了,递减就来个中位数

可以构造出一个这样的算法:

每次插入一个新的区间[i,i],使用a[i],表示当前点最好用自己的值就可以了

和前一个区间比较,如果这个点选用的值较小,进行区间合并,这个区间用它的中位数

那么就是动态询问区间中位数了,这个可以上主席树搞??

看到题解的大佬们都是上高贵的野兽可并堆,YY一下其实也简单,就是维护区间一半的数嘛

唯一有点玄学的是,维护区间较大的一半数会WA穿,要维护较小一半的数

原因:假如是较大的数,前面有可能有大量大数导致去掉了前面的大数而令后面的小一点的数被保留,而后面不可能有超过一半的数比前面的最大数要大否则后面的中位数一定会大于前面。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;

int cnt,L[1100000],R[1100000];
struct heap{int lc,rc,dep,tot;LL c;}h[1100000];int len,rt[1100000];
void add(int x,LL d)
{
    len++;
    h[len].lc=h[len].rc=0;h[len].c=d;
    h[len].dep=1;h[len].tot=1;
    
    rt[++cnt]=len;
    L[cnt]=R[cnt]=x;
}
int merge(int x,int y)
{
    if(x==0||y==0)return x+y;
    if(h[x].c<h[y].c)swap(x,y);
    
    h[x].tot+=h[y].tot;
    h[x].rc=merge(h[x].rc,y);
    if(h[h[x].lc].dep<h[h[x].rc].dep)swap(h[x].lc,h[x].rc);
    h[x].dep=h[h[x].lc].dep+1;
    return x;
}
void pop(int x){rt[x]=merge(h[rt[x]].lc,h[rt[x]].rc);}
void change(int x,int y)
{
    int num=(R[x]-L[x]+2)/2;//堆里面扔的数个数 
    rt[x]=merge(rt[x],rt[y]);
    while(h[rt[x]].tot>num)pop(x);
}

LL a[1100000];
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]), a[i]-=i;
    len=0;cnt=0;
    memset(rt,0,sizeof(rt));
    for(int i=1;i<=n;i++)
    {
        add(i,a[i]);
        while(cnt>1&&h[rt[cnt-1]].c>h[rt[cnt]].c)
            cnt--,R[cnt]=R[cnt+1],change(cnt,cnt+1);    
    }
    LL sum=0;
    for(int i=1;i<=cnt;i++)
        for(int j=L[i];j<=R[i];j++)
            sum+=abs(a[j]-h[rt[i]].c);
    printf("%lld\n",sum);
    
    return 0;
}

 

posted @ 2018-12-21 20:58  AKCqhzdy  阅读(169)  评论(0编辑  收藏  举报