首页 > 代码库 > 【左偏树+贪心】BZOJ1367-[Baltic2004]sequence

【左偏树+贪心】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 }

 

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