洛谷$P4331\ [BOI2004]\ Sequence$ 数字序列 左偏树

时间:2022-08-29 02:28:00

正解:左偏树

解题报告:

传送门$QwQ$

开始看到的时候$jio$得长得很像之前做的一个$dp$,,,

但是$dp$那题是说不严格这里是严格?

不难想到我们可以让$a_{i},b_{i}$同时减去$i$这样就变成那道题辣,,,?$QwQ$

但是如果$dp$的话复杂度是$O(n^2)$的就假了$QwQ$

这里介绍一个左偏树做法,复杂度是$O(nlogn)$的$QwQ$

先考虑两个特殊情况,分别是$a$递减和$a$递增$QwQ$?

递增很显然就$b_{i}=a_{i}$就成$QwQ$

然后如果是递减,小学奥数得当$b$为中位数时有$min$

然后现在考虑把$a$拆成若干个区间,考虑怎么合并$QwQ$

假如现在要合并的区间的$b$分别是$b_{1},b_{2}$

如果$b_{1}\leq b_{2}$,欧克直接合并就成

否则就考虑继续找中位数?

所以现在就变成了动态维护中位数$QwQ$

这个似乎在基础算法里港过?就说开两个堆然后不停地$push\ pop$就好$QwQ$

但是这题里面是要合并的嘛,所以就用左偏树就成?

具体来说就考虑,对长度为$x$的序列,就把前$\frac{x}{2}$小的数放到左偏树里面,每次合并就合并两个左偏树,中位数就是堆顶

$umm$然后其实我$jio$得代码实现还是有点儿难度的,,,?

所以我我我我给$code$里打了点儿注释$w$

然后记得开$ll$,,,$100pts->10pts$真的很难受,,,$QwQQQQQQ$

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define int long long
#define mp make_pair
#define gc getchar()
#define P pair<int,int>
#define ri register int
#define rc register char
#define rb register bool
#define t(i) edge[i].to
#define t_nw(i) edge_nw[i].to
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define e(i,x) for(ri i=head[x];i;i=edge[i].nxt)
#define e_nw(i,x) for(ri i=head_nw[x];i;i=edge_nw[i].nxt) const int N=+;
int n,a[N],tp[N],ed[N],top,ls[N],rs[N],dis[N],as;//tp是堆顶ed是范围 il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch<'' || ch>''))ch=gc;
if(ch=='-')ch=gc,y=;
while(''<=ch && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
int merg(ri x,ri y)
{
if(!x || !y)return x|y;
if(a[x]<a[y])swap(x,y);
rs[x]=merg(rs[x],y);if(dis[ls[x]]<dis[rs[x]])swap(ls[x],rs[x]);
dis[x]=dis[rs[x]]+;return x;
} signed main()
{
//freopen("4331.in","r",stdin);freopen("4331.out","w",stdout);
n=read();rp(i,,n)a[i]=read()-i;
rp(i,,n)
{
tp[++top]=i;ed[top]=i;
while(top> && a[tp[top]]<a[tp[top-]])
{
--top;tp[top]=merg(tp[top],tp[top+]);
if((ed[top+]-ed[top])& && (ed[top]-ed[top-])&)tp[top]=merg(ls[tp[top]],rs[tp[top]]);//可以用前面动态维护中位数的思考,就这个可并堆记的都只有1/2的小的那一半数嘛,然后如果合并的两个堆都是奇数,合并之后就会是偶数,就会要把最大的那个数弹到存大的那一半数的堆.因为这里并没开这个堆所以直接弹掉就成QwQ
ed[top]=ed[top+];
}
}
rp(i,,top)rp(j,ed[i-]+,ed[i])as+=abs(a[tp[i]]-a[j]);
printf("%lld\n",as);
rp(i,,top)rp(j,ed[i-]+,ed[i])printf("%lld ",a[tp[i]]+j);
return ;
}