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

【题目大意】

给定一个序列t1,t2,...,tn ,求一个递增序列z1<z2<...<zn , 使得R=|t1z1|+|t2z2|+...+|tnzn| 的值最小。本题中,我们只需要求出这个最小的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

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<stack>
  6 #include<cmath>
  7 using namespace std;
  8 const int MAXN=1000000+50;
  9 typedef long long ll;
 10 struct node
 11 {
 12     int key,dis,size;
 13     int lson,rson,father;
 14     int L,R;
 15 
 16 }ltree[MAXN];
 17 int n,z[MAXN];
 18 stack<int> S;
 19 
 20 void pushup(int x)
 21 {
 22     int l=ltree[x].lson,r=ltree[x].rson;
 23     ltree[x].size=1+ltree[l].size+ltree[r].size;
 24 } 
 25 
 26 void build(int rt,int x)
 27 {
 28     ltree[rt].key=x;
 29     ltree[rt].dis=(rt==0)?-1:0;
 30     ltree[rt].size=(rt==0)?0:1;
 31     //不要忘了当Rt=0的时候size为0 
 32     ltree[rt].lson=ltree[rt].rson=0;
 33     ltree[rt].father=ltree[rt].L=ltree[rt].R=rt;
 34 }
 35 
 36 int merge(int x,int y)
 37 {
 38     if (x==0 || y==0) return (x+y);
 39     if (ltree[x].key<ltree[y].key) swap(x,y);
 40     ltree[x].L=min(ltree[x].L,ltree[y].L);
 41     ltree[x].R=max(ltree[x].R,ltree[y].R);
 42     ltree[x].rson=merge(ltree[x].rson,y);
 43     int &l=ltree[x].lson,&r=ltree[x].rson;
 44     if (ltree[l].dis<ltree[r].dis) swap(l,r);
 45     if (r==0) ltree[x].dis=0;
 46         else ltree[x].dis=ltree[r].dis+1;
 47     pushup(x);
 48     return x;
 49 }
 50 
 51 int del(int rt)
 52 {
 53     int l=ltree[rt].lson,r=ltree[rt].rson;
 54     ltree[rt].dis=0;
 55     ltree[rt].size=1;
 56     ltree[rt].lson=ltree[rt].rson=0;
 57     int ret=merge(l,r);
 58     ltree[ret].L=ltree[rt].L;
 59     ltree[ret].R=ltree[rt].R;
 60     return ret;
 61 }
 62 
 63 void init()
 64 {
 65     scanf("%d",&n);
 66     for (int i=1;i<=n;i++) scanf("%d",&z[i]),z[i]-=i;
 67     for (int i=1;i<=n;i++) build(i,z[i]);
 68     build(0,0);
 69 } 
 70 
 71 void solve()
 72 {
 73     int before=z[1];
 74     S.push(1);
 75     for (int i=2;i<=n;i++)
 76     {
 77         int now=i;
 78         while (!S.empty())
 79         {
 80             int tail=S.top();
 81             if (ltree[now].key<ltree[tail].key)
 82             {
 83                 S.pop();
 84                 int tmp=merge(tail,now);
 85                 now=tmp;
 86                 while (ltree[now].size*2>(ltree[now].R-ltree[now].L+2)) now=del(now);
 87                 //不要忘记了这里是ltree[now].R-ltree[now].L+2,一开始写成了+1 
 88                 if (S.empty())
 89                 {
 90                     S.push(now);
 91                     break;
 92                 }
 93             }
 94             else
 95             {
 96                 S.push(now);
 97                 break;
 98             }
 99         }
100     }
101     ll ans=0;
102     while (!S.empty())
103     {
104         int now=S.top();S.pop();
105         for (int i=ltree[now].L;i<=ltree[now].R;i++) ans+=abs(z[i]-ltree[now].key);
106     }
107     printf("%lld",ans);
108 }
109 
110 int main()
111 {
112     init();
113     solve();
114     return 0;
115 }

 

posted @ 2016-09-03 10:41  iiyiyi  阅读(713)  评论(0编辑  收藏  举报