洛谷P4331 [BOI2004]Sequence 数字序列(左偏树)

传送门

 

感觉……不是很看得懂题解在说什么?

我们先把原数列$a_i-=i$,那么本来要求递增序列,现在只需要求一个非严格递增的就行了(可以看做最后每个$b_i+=i$,那么非严格递增会变为递增)

如果一个数列是递增的,一个一个相等的取,如果是递减的,取他们的中位数

前面的好理解,后面的想一下仓库运输那道题就明白了

然后我们现在把原数列分成了若干段答案相同的区间,考虑如何合并答案

如果$i$的答案小于等于$i+1$的答案,我们可以不做任何操作

那么考虑$i$的答案大于$i+1$的答案,就合并它们的元素,取新的中位数即可,这个过程可以用可并堆来优化

然后大概就这样了

 1 //minamoto
 2 #include<bits/stdc++.h>
 3 #define ll long long
 4 using namespace std;
 5 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 6 char buf[1<<21],*p1=buf,*p2=buf;
 7 template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
 8 inline int read(){
 9     #define num ch-'0'
10     char ch;bool flag=0;int res;
11     while(!isdigit(ch=getc()))
12     (ch=='-')&&(flag=true);
13     for(res=num;isdigit(ch=getc());res=res*10+num);
14     (flag)&&(res=-res);
15     #undef num
16     return res;
17 }
18 char sr[1<<21],z[20];int K=-1,Z;
19 inline void Ot(){fwrite(sr,1,K+1,stdout),K=-1;}
20 inline void print(int x){
21     if(K>1<<20)Ot();if(x<0)sr[++K]=45,x=-x;
22     while(z[++Z]=x%10+48,x/=10);
23     while(sr[++K]=z[Z],--Z);sr[++K]=' ';
24 }
25 const int N=1e6+5;
26 int n,top;ll ans=0;
27 struct node{
28     int l,r,sz,rt,val;
29     node(){}
30     node(int l,int r,int sz,int rt,int val):l(l),r(r),sz(sz),rt(rt),val(val){}
31 }st[N];
32 int d[N],L[N],R[N],v[N];
33 int merge(int x,int y){
34     if(!x||!y) return x+y;
35     if(v[x]<v[y]) swap(x,y);
36     R[x]=merge(R[x],y);
37     if(d[L[x]]<d[R[x]]) swap(L[x],R[x]);
38     d[x]=d[R[x]]+1;return x;
39 }
40 int erase(int x){return merge(L[x],R[x]);}
41 int main(){
42 //    freopen("testdata.in","r",stdin);
43     n=read();
44     for(int i=1;i<=n;++i) v[i]=read()-i;
45     st[top=1]=node(1,1,1,1,v[1]);
46     for(int i=2;i<=n;++i){
47         st[++top]=node(i,i,1,i,v[i]);
48         while(top!=1&&st[top-1].val>st[top].val){
49             --top;
50             st[top].rt=merge(st[top].rt,st[top+1].rt);
51             st[top].sz+=st[top+1].sz,st[top].r=st[top+1].r;
52             while(st[top].sz>(st[top].r-st[top].l+2)/2){
53                 --st[top].sz,st[top].rt=erase(st[top].rt);
54             }
55             st[top].val=v[st[top].rt];
56         }
57     }
58     for(int i=1,p=1;i<=n;++i){
59         if(st[p].r<i) ++p;
60         ans+=abs(st[p].val-v[i]);
61     }
62     printf("%lld\n",ans);
63     for(int i=1,p=1;i<=n;++i){
64         if(st[p].r<i) ++p;
65         print(st[p].val+i);
66     }
67     Ot();
68     return 0;
69 }

 

转载于:https://www.cnblogs.com/bztMinamoto/p/9806891.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值