您的位置:首页 > 产品设计 > UI/UE

【可并堆】BZOJ1367(Baltic2004)[sequence]题解

2018-01-08 11:46 405 查看

题目概述

给出 {an} ,选择一个上升序列 {bn} 使得 ∑ni=1|ai−bi| 最小,求最小值。

解题报告

对于这道题,我们可以将 {an} 改成 {an−n} ,然后就转化为求不下降序列 {bn} 。

如果 {an} 递减,那么 {bn} 显然取中位数最好。如果不递增,我们可以把序列划为若干段,每个段都取该段中位数(中位数不下降)。可以证明最优的 {bn} 一定是这样的形式。

对于 i ,我们先将其单独成段,然后检查前一段的中位数是不是 ≤ai ,如果不是,那么就把两段合并,求出新的中位数,直到满足。由此可见我们需要能快速求中位数,快速合并的数据结构来维护。由于前一段的中位数 ≤ai ,所以新的中位数不会比 ai 小,那么我们就可以用可并堆,只需要维护 si≤⌊num2⌋+1 ,堆顶就是中位数。

示例程序

#include<cstdio>
#include<cctype>
#include<algorithm>
typedef long long LL;
using namespace std;
const int maxn=1e6;

int n,v[maxn+5],ro[maxn+5],son[maxn+5][2],dis[maxn+5];
int tot,L[maxn+5],R[maxn+5],si[maxn+5];LL ans;

#define Eoln(x) ((x)==10||(x)==13||(x)==EOF)
inline char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
if (l==r) return EOF;return *l++;
}
inline int readi(int &x){
int tot=0,f=1;char ch=readc(),lst='+';
while (!isdigit(ch)) {if (ch==EOF) return EOF;lst=ch;ch=readc();}
if (lst=='-') f=-f;
while (isdigit(ch)) tot=(tot<<3)+(tot<<1)+ch-48,ch=readc();
return x=tot*f,Eoln(ch);
}
int Merge(int a,int b){
if (!a||!b) return a+b;if (v[a]<v[b]) swap(a,b);
int &l=son[a][0],&r=son[a][1];
r=Merge(r,b);if (dis[l]<dis[r]) swap(l,r);
return dis[a]=dis[r]+1,a;
}
#define Pop(ID) ro[ID]=Merge(son[ro[ID]][0],son[ro[ID]][1]),si[ID]--
inline int absi(int x) {if (x<0) return -x;return x;}
int main(){
freopen("program.in","r",stdin);
freopen("program.out","w",stdout);
readi(n);dis[0]=-1;for (int i=1;i<=n;i++) readi(v[i]),v[i]-=i;
for (int i=1;i<=n;i++){
ro[++tot]=i;L[tot]=i;R[tot]=i;si[tot]=1;
while (tot>1&&v[ro[tot-1]]>v[ro[tot]]){
tot--;ro[tot]=Merge(ro[tot],ro[tot+1]);
R[tot]=R[tot+1];si[tot]+=si[tot+1];
while (si[tot]>(R[tot]-L[tot]>>1)+1) Pop(tot);
}
}
for (int t=1;t<=tot;t++)
for (int i=L[t];i<=R[t];i++)
ans+=absi(v[i]-v[ro[t]]);
return printf("%lld\n",ans),0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: