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

【左偏树+贪心】BZOJ1367-[Baltic2004]sequence

2016-09-03 10:41 465 查看
【题目大意】

给定一个序列t1,t2,...,tn ,求一个递增序列z1<z2<...<zn , 使得R=|t1−z1|+|t2−z2|+...+|tn−zn| 的值最小。本题中,我们只需要求出这个最小的R值。

【思路】

-这个比加延迟标记的左偏树调试得还久……WA到死……

如果ti是递增的,我们只需要取zi=ti;

如果ti是递减的,我们只需要取ti的中位数。

所以我们将ti分割成若干个区间,维护每个区间的中位数。对于[L,R]的区间,我们存放[L,(L+R)/2]在堆中。具体如下操作:

(1)加入ti,将它作为一个单独的区间。

(2)比较前一个区间的中位数(即当前栈顶的最大值)和当前区间的中位数,如果前者小于后者,就将后者压入栈中。否则将前者弹出,和后者合并。注意的是如果两个区间的大小均为奇数(注意这里说的是区间大小,即L-R+1,而不是维护中位数的堆的大小),比如3和5合并,我们只需要存4个数字,而直接合并堆中存了5个,所以弹出堆顶。

(3)把合并后的堆作为当前区间,继续操作。

某种意义上的贪心思想。

我用的是左偏树,在左偏树里同时记录了L、R、size。

不过这样操作只会得到不下降,而不是递增。据说一开始输入t[i]时,t[i]-=i即可,没有会到意思orz

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<cmath>
using namespace std;
const int MAXN=1000000+50;
typedef long long ll;
struct node
{
int key,dis,size;
int lson,rson,father;
int L,R;

}ltree[MAXN];
int n,z[MAXN];
stack<int> S;

void pushup(int x)
{
int l=ltree[x].lson,r=ltree[x].rson;
ltree[x].size=1+ltree[l].size+ltree[r].size;
}

void build(int rt,int x)
{
ltree[rt].key=x;
ltree[rt].dis=(rt==0)?-1:0;
ltree[rt].size=(rt==0)?0:1;
//不要忘了当Rt=0的时候size为0
ltree[rt].lson=ltree[rt].rson=0;
ltree[rt].father=ltree[rt].L=ltree[rt].R=rt;
}

int merge(int x,int y)
{
if (x==0 || y==0) return (x+y);
if (ltree[x].key<ltree[y].key) swap(x,y);
ltree[x].L=min(ltree[x].L,ltree[y].L);
ltree[x].R=max(ltree[x].R,ltree[y].R);
ltree[x].rson=merge(ltree[x].rson,y);
int &l=ltree[x].lson,&r=ltree[x].rson;
if (ltree[l].dis<ltree[r].dis) swap(l,r);
if (r==0) ltree[x].dis=0;
else ltree[x].dis=ltree[r].dis+1;
pushup(x);
return x;
}

int del(int rt)
{
int l=ltree[rt].lson,r=ltree[rt].rson;
ltree[rt].dis=0;
ltree[rt].size=1;
ltree[rt].lson=ltree[rt].rson=0;
int ret=merge(l,r);
ltree[ret].L=ltree[rt].L;
ltree[ret].R=ltree[rt].R;
return ret;
}

void init()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&z[i]),z[i]-=i;
for (int i=1;i<=n;i++) build(i,z[i]);
build(0,0);
}

void solve()
{
int before=z[1];
S.push(1);
for (int i=2;i<=n;i++)
{
int now=i;
while (!S.empty())
{
int tail=S.top();
if (ltree[now].key<ltree[tail].key)
{
S.pop();
int tmp=merge(tail,now);
now=tmp;
while (ltree[now].size*2>(ltree[now].R-ltree[now].L+2)) now=del(now);
//不要忘记了这里是ltree[now].R-ltree[now].L+2,一开始写成了+1
if (S.empty())
{
S.push(now);
break;
}
}
else
{
S.push(now);
break;
}
}
}
ll ans=0;
while (!S.empty())
{
int now=S.top();S.pop();
for (int i=ltree[now].L;i<=ltree[now].R;i++) ans+=abs(z[i]-ltree[now].key);
}
printf("%lld",ans);
}

int main()
{
init();
solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: