【BZOJ1367】【Baltic2004】sequence - 可合并堆

题意:

题解:

其实这是道水题啦……只不过我没做过而已

先考虑构造不严格递增序列,考虑原序列中的一段下降区间,显然区间中的$z$全取中位数最优;

那么可以把原序列拆成很多个下降序列,从头到尾加入原序列中的数,每次把加进来的数看成一个新的下降区间,然后不断合并最后两个区间直到,最后一个区间的中位数不小于倒数第二个区间的中位数;

用可合并堆维护即可,左偏树啥的都行,我写的斜堆;

可合并堆如何维护区间中位数?只保留较小一半的数,则堆顶就是中位数;

要构造严格递增序列只需要把原序列中的每个数$t_i$减去$i$即可(显然我不会证);

代码:

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<queue>
 7 #define inf 2147483647
 8 #define eps 1e-9
 9 using namespace std;
10 typedef long long ll;
11 typedef double db;
12 struct node{
13     int ls,rs,v,siz;
14 }t[1000001];
15 int n,cnt=0,num[1000001],rts[1000001],R[1000001];
16 ll ans=0;
17 int merge(int x,int y){
18     if(!x||!y)return x|y;
19     if(t[x].v<t[y].v)swap(x,y);
20     t[x].siz+=t[y].siz;
21     t[x].rs=merge(t[x].rs,y);
22     swap(t[x].ls,t[x].rs);
23     return x;
24 }
25 int main(){
26     scanf("%d",&n);
27     for(int i=1;i<=n;i++){
28         scanf("%d",&num[i]);
29         num[i]-=i;
30         t[i].v=num[i];
31         t[i].siz=1;
32         cnt++;
33         rts[cnt]=R[cnt]=i;
34         while(cnt>1&&t[rts[cnt]].v<t[rts[cnt-1]].v){
35             R[cnt-1]=R[cnt];
36             cnt--;
37             rts[cnt]=merge(rts[cnt],rts[cnt+1]);
38             while(t[rts[cnt]].siz*2>R[cnt]-R[cnt-1]+1){
39                 rts[cnt]=merge(t[rts[cnt]].ls,t[rts[cnt]].rs);
40             }
41         }
42     }
43     for(int i=1,j=1;i<=cnt;i++){
44         for(;j<=R[i];j++){
45             ans+=abs(t[rts[i]].v-num[j]);
46         }
47     }
48     printf("%lld\n",ans);
49     return 0;
50 }
posted @ 2019-01-06 20:56 DCDCBigBig 阅读(...) 评论(...) 编辑 收藏