BZOJ 1588: [HNOI2002]营业额统计 Splay版(模板题)
1588: [HNOI2002]营业额统计
Time Limit: 5 Sec Memory Limit: 162 MBSubmit: 3863 Solved: 1199
[Submit][Status][Discuss]
Description
营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况。 Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额。分析营业情况是一项相当复杂的工作。由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题。经济管理学上定义了一种最小波动值来衡量这种情况: 该天的最小波动值 当最小波动值越大时,就说明营业情况越不稳定。 而分析整个公司的从成立到现在营业情况是否稳定,只需要把每一天的最小波动值加起来就可以了。你的任务就是编写一个程序帮助Tiger来计算这一个值。 第一天的最小波动值为第一天的营业额。 输入输出要求
Input
第一行为正整数 ,表示该公司从成立一直到现在的天数,接下来的n行每行有一个正整数 ,表示第i天公司的营业额。
Output
输出文件仅有一个正整数,即Sigma(每天最小的波动值) 。结果小于2^31 。
Sample Input
6
5
1
2
5
4
6
5
1
2
5
4
6
Sample Output
12
HINT
结果说明:5+|1-5|+|2-1|+|5-5|+|4-5|+|6-5|=5+4+1+0+1+1=12
Source
这题是我自己的第一道splay题,只是用来测试一下模板的。。。
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1588
分析:这题就是求前驱和后继。旋转跟SBT、AVL、BST、treap一样,并且不需要维护树的平衡,默认为90%的询问发生在10%的数据上面,所以我们在插入的时候就把插入的的数据经过splay操作旋转该节点至根部。这题在询问x的前驱的时候,我们可以先得到x,然后再把x经过splay操作旋转至根部,再找到根的前驱,后继同理。其他的操作以后再总结一下。。。
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define debug puts("here")
#define rep(i,n) for(int i=0;i<n;i++)
#define foreach(i,vec) for(unsigned i=0;i<vec.size();i++)
#define pb push_back
#define INF 1e9
namespace Splay{
#define X 1111111
#define px pre[x]
#define py pre[y]
#define lx ch[x][0]
#define ly ch[y][0]
#define lz ch[z][0]
#define rx ch[x][1]
#define ry ch[y][1]
int root,tot;
int ch[X][2],pre[X],val[X];
inline void init(){ // 初始化
root = tot = 0;
memset(ch,0,sizeof(ch));
memset(pre,0,sizeof(pre));
memset(val,0,sizeof(val));
}
inline void dfs(int x){ // debug使用
if(x){
dfs(lx);
printf("self = %d , left = %d , right = %d , father = %d\n",x,lx,rx,px);
dfs(rx);
}
}
inline void new_node(int &x,int father,int v){ // 构造新节点
x = ++tot;
pre[x] = father;
val[x] = v;
ch[x][0] = ch[x][1] = 0;
}
inline void setc(int y,int d,int x){ // 旋转过程的子树的链接
ch[y][d] = x;
pre[x] = y;
}
inline int sgn(int x){ // 0表示在左,1表示在右
return ch[px][1]==x;
}
inline void _rot(int x,int d){
int y = px;
int z = py;
setc(y,!d,ch[x][d]);//类似SBT,要把其中一个分支先给父节点
if(z) //如果父节点不是根结点,则要和父节点的父节点连接起来
setc(z,sgn(y),x);
pre[x] = z;
setc(x,d,y);
}
inline void rot(int x){_rot(x,!sgn(x));}
inline void zag(int x){_rot(x,0);} // 左旋
inline void zig(int x){_rot(x,1);} // 右旋
// Splay调整,将根为r的子树调整为goal
inline int splay(int x,int goal=0){
while(px!=goal){
int y = px;
int z = py;
if(z==goal){
rot(x);
break;
}
if(lz==y){
if(ly==x)
zig(y),zig(x);
else
zag(x),zig(x);
}
else{
if(ry==x)
zag(y),zag(x);
else
zig(x),zag(x);
}
}
if(goal==0)
root = x;
return x;
}
inline int insert(int v){ // 插入
int x = root;
while(ch[x][ val[x]<v ]){
if(val[x]==v){
splay(x); // 已存在,这题可以忽略掉,但是需要旋转该节点作为根
return 0;
}
x = ch[x][ val[x]<v ];
}
new_node(ch[x][ val[x]<v ],x,v);
splay(ch[x][ val[x]<v ]); // 新插入节点splay至根部
return 1;
}
inline int get_pre(int x){ // 得到前驱
int tmp = x;
x = lx;
if(x==0)
return INF;
while(rx)
x = rx;
return val[tmp]-val[x];
}
inline int get_next(int x){ // 得到后继
int tmp = x;
x = rx;
if(x==0)
return INF;
while(lx)
x = lx;
return val[x]-val[tmp];
}
#undef X
#undef px
#undef py
#undef lx
#undef ly
#undef lz
#undef rx
#undef ry
}using namespace Splay;
int main(){
#ifndef ONLINE_JUDGE
freopen("sum.in","r",stdin);
#endif
int ans = 0,n,x;
cin >> n;
init();
rep(i,n){
if(scanf("%d",&x)==EOF)
x = 0;
if(i==0){
ans += x;
new_node(root,0,x);
continue;
}
if(insert(x)==0)
continue;
// 前面插入的时候x已经splay至根部,所以可以直接求值
ans += min(get_pre(root),get_next(root));
}
cout<<ans<<endl;
return 0;
}

浙公网安备 33010602011771号