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

bzoj 1367: [Baltic2004]sequence(中位数+可并堆)

2016-05-08 17:16 661 查看

1367: [Baltic2004]sequence

Time Limit: 20 Sec Memory Limit: 64 MB

Submit: 935 Solved: 351

[Submit][Status][Discuss]

Description



Input



Output

一个整数R

Sample Input

7

9

4

8

20

14

15

18

Sample Output

13

HINT

所求的Z序列为6,7,8,13,14,15,18.

R=13

Source



[Submit][Status][Discuss]

题解:中位数+可并堆

分情况讨论一下:

一个区间[l, r],递增的,则对于此区间最优解为z[i] = t[i];

一个区间[l,r] ,递减的,则z[l] = z[l + 1] = ... = z[r] = 这段数的中位数,不妨叫做w。(中位数为第(r - l + 1) / 2大的数),为什么是中位数呢,因为区间是递减的,我们要把他变成不下降的,最小的改动情况就是把他改成一个每个数都相同的序列,那么这就转化成了中位数问题,因为所有点到中位数的距离是最小的。但是我们要求的是上升序列而不是不下降序列,所以需要将将v[i]处理成v[i]-i。
那么这样就可以做了,对于递增的区间,我们可以把区间中的每一个数看成一个单独的堆,那么这个堆维护的中位数就是他本身。如果要加入一个数,我们可以把这个点先看成一个单独的堆,如果v[root[now]]<v[root[now]]也就是当前这个点,比他的前一个区间的中位数要小,但是我们要求他递增,那么这个问题又转换成了“一个区间[l,r]
,递减的,则z[l] = z[l + 1] = ... = z[r] = 这段数的中位数”这个问题,我们可以用可并堆来将两个合并到一起。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 1000003
using namespace std;
int n,m;
int v
,l
,r
,R
,L
,size
,tot
,root
,d
;
int merge(int x,int y)
{
if (!x) return y;
if (!y) return x;
if (v[x]<v[y]) swap(x,y);
R[x]=merge(R[x],y);
size[x]=size[L[x]]+size[R[x]]+1;
if (d[L[x]]<=d[R[x]])  swap(L[x],R[x]);
d[x]=d[R[x]]+1;
return x;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&v[i]),v[i]=v[i]-i;
int now=0;
for (int i=1;i<=n;i++)
{
now++;
root[now]=i;
l[now]=r[now]=i; //所控制区间的左右端点
tot[now]=1;//当前堆维护的中位数控制的数的个数
size[root[now]]=1;//堆中元素的个数
while (now>1&&v[root[now-1]]>v[root[now]])
{
now--;
r[now]=r[now+1]; tot[now]+=tot[now+1];
root[now]=merge(root[now],root[now+1]);
while (size[root[now]]*2>tot[now]+1)//保证堆顶元素是当前区间的中位数
root[now]=merge(L[root[now]],R[root[now]]);
}
}
long long ans=0;
for (int i=1;i<=now;i++)
for (int j=l[i];j<=r[i];j++)
ans+=(long long)abs(v[j]-v[root[i]]);
printf("%I64d\n",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: