所求的Z序列为6,7,8,13,14,15,18.
R=13
可并堆,思路很好
分享一篇题解:http://www.cnblogs.com/rausen/p/4033724.html
说一下如何用可并堆维护区间中位数。
维护一个大根堆,堆里的元素个数等于区间长度的一半,里面保存的数是区间中较小的一半数。那么很显然中位数就是堆顶元素。
两个区间合并的时候,把两个堆合并,并将多余的元素弹出,这样就可以维护区间中位数了。
其实维护小根堆也是可以的,区别是要维护区间中较大的一半数。
据说还有一个小技巧(表示自己没有理解这里):
这样求出来的z[i]是单调不减,并不保证单调递增。
开始的时候将每一个t[i]减去i,然后按照该方法计算z[i],这样就相当于所有z[i]都加上了i,就把不减转化成了递增。(然而这为什么是对的呢...求助大神)
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define N 1000005
using namespace std;
int n,m,tot,a[N],rt[N],l[N],r[N];
ll ans;
struct HEAP
{
int cnt,l[N],r[N],v[N],d[N],sz[N];
int merge(int x,int y)
{
if (!x||!y) return x+y;
if (v[x]<v[y]) swap(x,y);
r[x]=merge(r[x],y);
if (d[l[x]]<d[r[x]]) swap(l[x],r[x]);
d[x]=d[r[x]]+1;
sz[x]=sz[l[x]]+sz[r[x]]+1;
return x;
}
void pop(int &x)
{
x=merge(l[x],r[x]);
}
int new_heap(int x)
{
cnt++;
v[cnt]=x;
sz[cnt]=1;
l[cnt]=r[cnt]=0;d[cnt]=1;
return cnt;
}
}heap;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int main()
{
n=read();
F(i,1,n) a[i]=read()-i;
F(i,1,n)
{
tot++;
rt[tot]=heap.new_heap(a[i]);
l[tot]=r[tot]=i;
while (tot>1&&heap.v[rt[tot-1]]>heap.v[rt[tot]])
{
tot--;
rt[tot]=heap.merge(rt[tot],rt[tot+1]);
r[tot]=r[tot+1];
while (heap.sz[rt[tot]]>(r[tot]-l[tot]+2)/2) heap.pop(rt[tot]);
}
}
F(i,1,tot)
{
int tmp=heap.v[rt[i]];
F(j,l[i],r[i]) ans+=abs(a[j]-tmp);
}
printf("%lld\n",ans);
return 0;
}
