bzoj1367【Baltic2004】sequence

1367: [Baltic2004]sequence

Time Limit: 20 Sec   Memory Limit: 64 MB
Submit: 953   Solved: 362
[ 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




可并堆,思路很好

分享一篇题解: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;
}


你可能感兴趣的:(bzoj)