1367: [Baltic2004]sequence
Time Limit: 20 Sec Memory Limit: 64 MBSubmit: 631 Solved: 215
[ Submit][ Status]
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
左偏树。
详细题解(在P13)。
这个题解求的z是不减的,要求单增的话只要把a[i]减i再算即可。
很巧妙的是用左偏树来合并两个区间的中位数。
根据题解,前一个区间的中位数大于后一个区间的中位数,合并之后中位数只会变小,因此先把两个对合并(大根堆),然后把最大的一个个除去,最后剩下一半即可,最大的那个就是中位数。
一定要注意,这里的“一半”一定要向上取整!!因为如果两个区间原本都是3个,向下取整都变成1个,合并之后6个只剩下2个了。。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#define M 1050000
using namespace std;
int n,a[M],root[M],l[M],r[M],w[M],tot=0,cnt=0;
struct Ltree
{
int size,l,r,dis,v;
}t[M];
void read(int &tmp)
{
tmp=0;
char ch=getchar();
int fu=1;
for (;ch<'0'||ch>'9';ch=getchar())
if (ch=='-') fu=-1;
for (;ch>='0'&&ch<='9';ch=getchar())
tmp=tmp*10+ch-'0';
tmp*=fu;
}
int New_heap(int x)
{
t[x].v=a[x];
t[x].l=t[x].r=t[x].dis=0;
t[x].size=1;
return x;
}
int Merge(int x,int y)
{
if ((!x)||(!y)) return x+y;
if (t[x].v<t[y].v)
swap(x,y);
t[x].size+=t[y].size;
t[x].r=Merge(t[x].r,y);
if (t[t[x].l].dis<t[t[x].r].dis)
swap(t[x].l,t[x].r);
t[x].dis=t[t[x].r].dis+1;
return x;
}
int Del(int x)
{
return Merge(t[x].l,t[x].r);
}
int main()
{
read(n);
for (int i=1;i<=n;i++)
read(a[i]),a[i]-=i;
tot=0;
t[0].dis=-1;
for (int i=1;i<=n;i++)
{
root[++tot]=New_heap(i);
w[tot]=a[i];
l[tot]=r[tot]=i;
while (tot>1&&w[tot-1]>w[tot])
{
tot--;
r[tot]=r[tot+1];
root[tot]=Merge(root[tot],root[tot+1]);
int x=(r[tot]-l[tot]+2)/2;
while (t[root[tot]].size>x)
root[tot]=Del(root[tot]);
w[tot]=t[root[tot]].v;
}
}
long long ans=0LL;
for (int i=1;i<=tot;i++)
for (int j=l[i];j<=r[i];j++)
ans+=abs(a[j]-w[i]);
printf("%lld\n",ans);
return 0;
}