[poj] 3666 Making the Grade

原题

左偏树裸题(也可以用dp做)

对于一段有连续性的不合法区间,把他们全都变成中位数显然是最好的。
假如我们要实现连续不下降,那么:
对于后一段区间的中位数比前一段的中位数小,那么把这两段区间合并,全部修改为这两段区间的中位数,否则的话就不用管。
假如我们要实现连续不上升,那么我们把小于号改为大于号就好了……

#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 2020
using namespace std;
int n,a[N],ans,tmp,q[N],b[N],l,now;
struct node
{
    node *ls,*rs;
    int val,dist,siz;
    node(): ls(NULL),rs(NULL),val(0),dist(0),siz(0){}
    node(int x): ls(NULL),rs(NULL),val(x),dist(0),siz(1){}
    node* update()
	{
	    if (!ls || ls->dist<rs->dist) swap(ls,rs);
	    dist=rs?rs->dist+1:0;
	    siz=(ls?ls->siz:0)+(rs?rs->siz:0)+1;
	    return this;
	}
}*tre[N];

int read()
{
    int ans=0,fu=1;
    char j=getchar();
    for (;(j<'0' || j>'9') && j!='-';j=getchar()) ;
    if (j=='-') fu=-1,j=getchar();
    for (;j>='0' && j<='9';j=getchar()) ans*=10,ans+=j-'0';
    return ans*fu;
}

node* merge(node *x,node*y)
{
    if (!x) return y;
    if (!y) return x;
    if (x->val<y->val) swap(x,y);
    x->rs=merge(x->rs,y);
    return x->update();
}

void pop(node *&x,int t)
{
    while ((x->siz)>((t+1)/2))
	x=merge(x->ls,x->rs);
}

int main()
{
    n=read();
    for (int i=1;i<=n;i++) a[i]=read();
    for (int i=1;i<=n;i++)
    {
	b[i]=a[i];
	tre[i]=new node(b[i]);
	while (l && b[i]<b[q[l]])
	{
	    tre[i]=merge(tre[i],tre[q[l]]);
	    --l;
	    pop(tre[i],i-q[l]);
	    b[i]=tre[i]->val;
	}
	q[++l]=i;
    }
    now=1;
    l=1;
    while (now<=n)
    {
	b[now]=b[q[l]];
	if (now==q[l]) ++l;
	++now;
    }
    for (int i=1;i<=n;i++) ans+=abs(a[i]-b[i]);
    memset(tre,0,sizeof(tre));
    l=0;
    for (int i=1;i<=n;i++)
    {
	b[i]=a[i];
	tre[i]=new node;
	while (l && b[i]>b[q[l]])
	{
	    tre[i]=merge(tre[i],tre[q[l]]);
	    --l;
	    pop(tre[i],i-q[l]);
	    b[i]=tre[i]->val;
	}
	q[++l]=i;
    }
    now=1;
    l=1;
    while (now<=n)
    {
	b[now]=b[q[l]];
	if (now==q[l]) ++l;
	++now;
    }
    for (int i=1;i<=n;i++) tmp+=abs(a[i]-b[i]);
    printf("%d",min(ans,tmp));
    return 0;
}
posted @ 2017-11-26 10:57  Mrha  阅读(132)  评论(0编辑  收藏  举报