(HYSBZ)BZOJ 1588 营业额统计
Time Limit: 5000MS | Memory Limit: 165888KB | 64bit IO Format: %lld & %llu |
Description
营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况。 Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额。分析营业情况是一项相当复杂的工作。由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题。经济管理学上定义了一种最小波动值来衡量这种情况: 该天的最小波动值 当最小波动值越大时,就说明营业情况越不稳定。 而分析整个公司的从成立到现在营业情况是否稳定,只需要把每一天的最小波动值加起来就可以了。你的任务就是编写一个程序帮助Tiger来计算这一个值。 第一天的最小波动值为第一天的营业额。 输入输出要求
Input
第一行为正整数 ,表示该公司从成立一直到现在的天数,接下来的n行每行有一个正整数 ,表示第i天公司的营业额。
Output
输出文件仅有一个正整数,即Sigma(每天最小的波动值) 。结果小于2^31 。
Sample Input
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
HNOI2002
/****Spaly的基本操作和应用**** 求前驱与root的差和后驱与root的差,取最大,累加即可. Rotate写的很精简,是学习别人的; = =!他的源码有错误,不过却又不影响结果...... 以后看别人的东西也要小心~~ *****************************/ #include <map> #include <set> #include <list> #include <queue> #include <stack> #include <cmath> #include <ctime> #include <vector> #include <bitset> #include <cstdio> #include <string> #include <numeric> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <functional> using namespace std; typedef long long ll; typedef unsigned long long ull; #define eps 1e-8 #define inf 0x7fffffff #define debug puts("BUG") #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define read freopen("in.txt","r",stdin) #define write freopen("out.txt","w",stdout) #define Mem(a,x) memset(a,x,sizeof(a)) #define maxn 100005 int pre[maxn],key[maxn],ch[maxn][2],root,tot1; //分别表示父结点,键值,左右孩子(0为左孩子,1为右孩子),根结点,结点数量 int n; int ans; void init() { root=tot1=0; ans=0; Mem(key,0); } void NewNode(int &r,int father,int k) { r=++tot1; //记录新节点的位置 pre[r]=father;//更新新节点的父亲 key[r]=k; ch[r][0]=ch[r][1]=0;//左孩子和右孩子为空 } void Rotate(int x,int k)//k为左旋和右旋的标志 { int y=pre[x];/**记录x的父亲**/ ch[y][!k]=ch[x][k]; pre[ch[x][k]]=y; /**如果k=0, 表示从左到右旋转;如果k=1,表示从右到左旋转 *k=0, 表示x的位置由它的右孩子代替; *k=1, 表示x的位置由它的左孩子代替. *x的位置, 即为 !k **/ if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x; pre[x]=pre[y]; /** 更新x的父亲 和 x的新父亲的孩子节点(如果新的父亲存在) * 如上 **/ ch[x][k]=y; pre[y]=x; /**更新y的父亲(即为x) 和 x的孩子节点 *如上 **/ } void Splay(int r,int goal)//Splay调整,将r的值调整到目标位置goal { while(pre[r]!=goal) { if(pre[pre[r]]==goal)//如果r的父亲就是根节点,goal的值为0,直接进行一次旋转即可 { Rotate(r,ch[pre[r]][0]==r); } else//否则 { int y= pre[r]; int k= ch[pre[y]][0]==y;//记录y与y的父亲的方向 if(ch[y][k]==r)//判断x与y和y的父亲是否在同一方向 { //是 , 进行一字型旋转 Rotate(r,!k);//先左(右)旋 Rotate(r, k);//后右(左)旋 } else { //否 , 进行之字形旋转 Rotate(y,k);//先对y进行左(右)旋 Rotate(r,k);//后对x进行右(左)旋 } } } /**更新根节点**/ if(goal==0) root=r; } int Insert(int k) //将键值k插入到Splay中(Splay不为空) { int r=root; //从根节点开始 while(ch[r][key[r]<k]) { //一直向下,找到叶子节点,根据二叉排序树的性质,查找时只需要比较key[r]与k的大小 if(key[r]==k) { //同一个值不需要再次插入,只需要进行Splay操作 Splay(r,0); return 0; //表示插入失败 } r=ch[r][key[r]<k]; //未找到叶子节点,就需继续向下找 } if(key[r]==k) { //同一个值不需要再次插入,只需要进行Splay操作 Splay(r,0); return 0; //表示插入失败 } NewNode(ch[r][k>key[r]],r,k); //新建节点 Splay(ch[r][k>key[r]],0); //Slay操作,插入的节点调整至根节点 return 1; //表示插入成功 } int Get_pre(int x)//寻找前驱,即寻找左子树的最右点 { int temp=ch[x][0]; if(temp==0) return inf; while(ch[temp][1]) temp=ch[temp][1]; return key[x]-key[temp]; } int Get_next(int x)//寻找后继,即寻找右子树的最左点 { int temp=ch[x][1]; if(temp==0) return inf; while(ch[temp][0]) temp=ch[temp][0]; return key[temp]-key[x]; } /********************DEBUG***************************/ void Puts() { printf("root: %d\n",root); for(int i=1;i<=tot1;i++) { printf("node: %d ,l is %d ,r is %d ,key : %d\n",i,ch[i][0],ch[i][1],key[i]); } } /********************DEBUG END***********************/ int main() { while(~scanf("%d",&n)) { init(); for(int i=1; i<=n; i++) { int x; scanf("%d",&x); if(i==1) { ans+=x; NewNode(root,0,x);//建立Splay的第一个节点 continue; } if(Insert(x)==0)//插入,已经存在的值不需要插入 {continue;} int a=Get_pre(root);//与前驱的差,可能不存在 int b=Get_next(root);//与后驱的差,也可能不存在 ans+=min(a,b); } printf("%d\n",ans); } return 0; }