【左偏树+贪心】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
给定一个序列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; }
相关文章推荐
- BZOJ 1367([Baltic2004]sequence-左偏树+中位数贪心)
- BZOJ 1367([Baltic2004]sequence-左偏树+中位数贪心)
- BZOJ 1367([Baltic2004]sequence-左偏树+中位数贪心)
- bzoj 1367: [Baltic2004]sequence 左偏树+贪心
- BZOJ 1367: [Baltic2004]sequence 左偏树
- bzoj 1367: [Baltic2004]sequence 左偏树
- BZOJ1367 [Baltic2004]sequence 【左偏树】
- 【BZOJ 1367】 1367: [Baltic2004]sequence (可并堆-左偏树)
- 【BZOJ1367】[Baltic2004]sequence 左偏树
- BZOJ[1367][Baltic2004]Sequence 可并堆
- BZOJ1367 [Baltic2004]sequence
- 【可并堆】BZOJ1367(Baltic2004)[sequence]题解
- [可并堆 中位数] BZOJ 1367 [Baltic2004]sequence
- BZOJ 1367: [Baltic2004]sequence
- 【BZOJ1367】【Baltic2004】sequence
- 1367:[Baltic2004]sequence 左偏树
- 【BZOJ 1367】 [Baltic2004]sequence 可并堆
- 可并堆试水--BZOJ1367: [Baltic2004]sequence
- bzoj 1367: [Baltic2004]sequence
- BZOJ 1367: [Baltic2004]sequence [可并堆 中位数]