BZOJ_1367_[Baltic2004]sequence_结论题+可并堆
BZOJ_1367_[Baltic2004]sequence_结论题+可并堆
Description
Input
Output
一个整数R
Sample Input
7
9
4
8
20
14
15
18
9
4
8
20
14
15
18
Sample Output
13
HINT
所求的Z序列为6,7,8,13,14,15,18.
R=13
神题。有一个结论:最优解一定是把序列分成m块,每块取中位数,并使得取出的序列递增。
不会证?https://wenku.baidu.com/view/20e9ff18964bcf84b9d57ba1.html
然后发现后面的合法中位数递增,搞来一个可并大根堆来维护,当堆内元素超过序列的一半时弹出。
然而结论要求序列不降,这里求序列递增。
我们发现每个递增的序列的每个数$ai$ 减去$i$ 对应着唯一一个不降序列,故在一开始对序列进行处理。
代码:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; char nc() { static char buf[100000],*p1,*p2; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline int rd() { register int x=0; register char s=nc(); while(s<'0'||s>'9') s=nc(); while(s>='0'&&s<='9') x=x*10+s-'0',s=nc(); return x; } int fabs(int x){return x>0?x:-x;} #define N 1000050 typedef long long ll; int n,a[N],root[N],siz[N],val[N],ls[N],rs[N],dis[N],lp[N],rp[N]; ll ans; int merge(int x,int y) { if(!x) return y; if(!y) return x; if(val[x]<val[y]) swap(x,y); siz[x]+=siz[y]; rs[x]=merge(rs[x],y); if(dis[rs[x]]>dis[ls[x]]) swap(ls[x],rs[x]); dis[x]=dis[rs[x]]+1; return x; } int main() { n=rd(); int i,tot=0,m=0,j; for(i=1;i<=n;i++) a[i]=rd(),a[i]-=i; dis[0]=-1; for(i=1;i<=n;i++) { root[++m]=++tot; val[tot]=a[i]; siz[tot]=1; lp[m]=rp[m]=i; while(m>1&&val[root[m]]<val[root[m-1]]) { m--; root[m]=merge(root[m],root[m+1]); rp[m]=rp[m+1]; while(2*siz[root[m]]>rp[m]-lp[m]+2) root[m]=merge(ls[root[m]],rs[root[m]]); } } for(i=1;i<=m;i++) { for(j=lp[i];j<=rp[i];j++) { ans+=fabs(val[root[i]]-a[j]); } } printf("%lld\n",ans); }