结果说明:5+|1-5|+|2-1|+|5-5|+|4-5|+|6-5|=5+4+1+0+1+1=12
序言:纪念我的第一道splay的题目~~~~~~~~~~~
简述题意:输入N天的营业额,求每天的营业最小波动值的和。第i天的营业最小波动值=min(abs(a[j]-a[i]))(1<=j<i)。
很容易想到n^2的暴力,当然也很容易发现不行。于是不知不觉就想到了BST。但是数据会卡啊。如果全都是一模一样的数字,或者是递减、递增,BST和n^2也差不了多少!
于是,我们的splay横空出世!在前段时间我已经研究了它的基本概念,现在开始敲代码。
首先要明确一点:如何求某一天的最小波动值。设该点时A,在插入到BST后,我们先把这个顶点调到根节点。那么在它的左子树中,所有值都比它小;我们要使比它小的值尽量大,就要不断往左子树中的右结点中找一个值X。同理,在它的右子树中,所有值都比它大;我们要使比它大的值尽量小,就要不断往右子树中的左结点中找一个值Y。那么,该A点的最小波动值=min(A-X,Y-X)。再开个累加器即可。
以下是代码:
#include<stdio.h>
#include<cmath>
using namespace std;
const int maxn=40001;
const int oo=2100000000;
int father[maxn],left[maxn],right[maxn],num[maxn]; //num[i]表示第i天的营业额。
int n,a,i,lef,righ,root;
long long ans;
int min(int a,int b)
{
if (a<b) return a;
return b;
}
void left_up(int x) //zig——左旋
{
int y=father[x];right[y]=left[x];
if (left[x]!=0) father[left[x]]=y;
int fa=father[y];
father[x]=fa;
if (fa!=0)
{
if (left[fa]==y) left[fa]=x;else right[fa]=x;
}
father[y]=x;left[x]=y;
}
void right_up(int x) //zag——右旋
{
int y=father[x];left[y]=right[x];
if (right[x]!=0) father[right[x]]=y;
int fa=father[y];
father[x]=fa;
if (fa!=0)
{
if (left[fa]==y) left[fa]=x;else right[fa]=x;
}
father[y]=x;right[x]=y;
}
void splay(int x) //把x结点旋转到根节点。
{
while (father[x]!=0)
{
int y=father[x];
if (father[y]==0)
{
if (x==left[y]) right_up(x);else left_up(x);
break;
}
int fa=father[y];
if (x==left[y])
{
if (y==left[fa]){right_up(y);right_up(x);}
else {right_up(x);left_up(x);}
}
else
{
if (y==right[fa]) {left_up(y);left_up(x);}
else {left_up(x);right_up(x);}
}
}
root=x;
}
void insert(int k) //把k结点插到BST里。
{
if (a<num[k])
{
if (left[k]==0)
{
left[k]=i;
father[i]=k;
num[i]=a;
}
else insert(left[k]);
}
else
{
if (right[k]==0)
{
right[k]=i;
father[i]=k;
num[i]=a;
}
else insert(right[k]);
}
}
int search_1(int k) //找左节点
{
if (left[k]!=0) return search_1(left[k]);
else return num[k];
}
int search_2(int k) //找右结点
{
if (right[k]!=0) return search_2(right[k]);
else return num[k];
}
int main()
{
scanf("%ld",&n);
scanf("%ld",&a);
father[1]=0;left[1]=0;right[1]=0;num[1]=a;ans=a;root=1;
for (i=2;i<=n;i++)
{
if (scanf("%ld",&a)==EOF) a=0;
insert(root);splay(i);
int lef=left[root]!=0?search_2(left[root]):-oo;
int righ=right[root]!=0?search_1(right[root]):oo;
if (lef==-oo) ans+=righ-a;else if (righ==oo) ans+=a-lef;else ans+=min(a-lef,righ-a);
}
printf("%lld",ans);
return 0;
}