左偏树(p4431)

Posted pangbi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了左偏树(p4431)相关的知识,希望对你有一定的参考价值。

难得不是左偏树,而是思维;

这道题在做得时候,有两个性质

1.如果a是一个不下降序列,那么b[i]==a[i]时取得最优解。

2.如果a是一个严格递减序列,则取a序列的中位数x,令b[1]=b[2]=b[3]=...=b[n]=x,即是最优解。

于是在做得时候,我们会分为几个区间,通过区间得合并去做这一道题;

我们根据这两个性质,求出这些区间的最优质,去合并;

三.考虑一般情况

a序列一定不可能这么良心是上面的两种情况。

但它一定是由这两种情况组成的,也就是把a序列看成一段一段的,每一段要么不下降,要么严格递减。

那么要分别计算出每一段的答案是很容易的。

问题是要保证b序列不下降,所以该怎么合并答案呢?

这里又有一个结论:

把两段合在一起,取一个新的中位数就行了=。=

道理是同上的。

 

四.具体操作

1.初始令每一段的长度为1,令中位数为ci,则ci = ai,然后一段一段的合并起来。

若ci <= ci+1,那么就保持不变;否则将ci和ci+1所在的区间合并,取一个新的中位数,作为新区间的答案。

.........................................................................................................................................

2.这里会出现一个问题,就是第一次合并时,有可能ci+1>=ci,没有把两个区间并起来取中位数。

但是可能后面的那个区间又和其他区间合并了,中位数变小了,以至于还要和前一个区间合并。

其实很简单qwq,用栈维护一下就好了。

.........................................................................................................................................

3.那么问题来了,怎么求中位数呢?求了中位数还要把两段区间合并起来?

(下面一段话引用于某dalao博客)

因此我们需要一个数据结构,支持合并、查询最大值和删除。

为什么要查询最大值和删除呢?因为维护中位数可以只维护⌈1/2区间长度⌉小的数,用一个大根堆,则堆顶就是中位数。

合并完两个区间后,就一直删除堆顶,直到元素个数 = ⌈1/2区间长度⌉。

显然是用左偏树啦qwq。(参照别人博客)

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<math.h>
 4 #include<string.h>
 5 using namespace std;
 6 typedef long long ll;
 7 const ll maxn=1e6+10;
 8 ll val[maxn];
 9 ll dis[maxn];
10 struct node{
11     ll rt,l,r,siz;
12     ll w;
13 }G[maxn]; ll num=0;
14 ll ch[maxn][2];
15 ll b[maxn];
16 ll Merge(ll x,ll y)
17 {
18     if(!x||!y) return x+y;
19     if(val[x]<val[y]) swap(x,y);
20     ch[x][1]=Merge(ch[x][1],y);
21     if(dis[ch[x][0]]<dis[ch[x][1]]) swap(ch[x][0],ch[x][1]);
22     dis[x]=dis[ch[x][1]]+1;
23     return x;
24 }
25 int main()
26 {
27     ll n;
28     scanf("%lld",&n);
29     for(ll i=1;i<=n;i++){
30         scanf("%lld",&val[i]);
31         val[i]-=i;
32     }
33     for(ll i=1;i<=n;i++){
34         G[++num]=(node) {i,i,i,1,val[i] };
35         while(num>1&&G[num].w<G[num-1].w){
36             num--;
37             G[num].rt=Merge(G[num].rt,G[num+1].rt);
38             G[num].siz+=G[num+1].siz;
39             G[num].r=G[num+1].r;
40             while(G[num].siz>(G[num].r-G[num].l+1+2)>>1){
41                 G[num].siz--;
42                 ll t=G[num].rt;
43                 G[num].rt=Merge(ch[t][0],ch[t][1]);
44             }
45             G[num].w=val[G[num].rt];
46         }
47     }
48     ll ans=0;
49     for(ll i=1;i<=num;i++){
50         for(ll j=G[i].l;j<=G[i].r;j++){
51             b[j]=G[i].w;
52             ans+=fabs(val[j]-b[j]);
53         }
54     }
55     printf("%lld
",ans);
56     for(ll i=1;i<=n;i++){
57         printf("%lld ",b[i]+i);
58     }
59     return 0;
60 }

 

以上是关于左偏树(p4431)的主要内容,如果未能解决你的问题,请参考以下文章

左偏树教程

P3273 [SCOI2011]棘手的操作 左偏树

左偏树

左偏树

左偏树的关系

并不对劲的左偏树