HNOI2002 营业额统计(splay tree)

【描述 Description】
  营业额统计
Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况。
Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额。分析营业情况是一项相当复杂的工作。由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题。经济管理学上定义了一种最小波动值来衡量这种情况:
该天的最小波动值=min{|该天以前某一天的营业额-该天的营业额|}
当最小波动值越大时,就说明营业情况越不稳定。
而分析整个公司的从成立到现在营业情况是否稳定,只需要把每一天的最小波动值加起来就可以了。你的任务就是编写一个程序帮助Tiger来计算这一个值。
第一天的最小波动值为第一天的营业额。
【输入格式 Input Format】  
  第一行为正整数n(n<=32767) ,表示该公司从成立一直到现在的天数,接下来的n行每行有一个正整数a(<=1000000) ,表示第i天公司的营业额。
     
     
  【输出格式 Output Format】  
  输出文件仅有一个正整数,即Sigma(每天最小的波动值) 。结果小于2^31 。
     
     
  【样例输入 Sample Input】  
  6
5
1
2
5
4
6
     

【样例输出 Sample Output】

12

     
  时间限制 Time Limitation  
  各个测试点1s
注释 Hint

  结果说明:5+|1-5|+|2-1|+|5-5|+|4-5|+|6-5|=5+4+1+0+1+1=12

方法1:也可以先将所有数据输入进来,然后将其下标按照数据的大小排序O(nlog2n),然后对每一个数,向两边搜索第一个下标小于自己的数,并计算差值,即他与前驱和后继的差值(绝对值)去较小的。显然TLE。。。那就用强大的splay吧!

 伸展树其实就是一种二叉排序树,只是它并不追求树的平衡,而是在每一次插入元素的时候,都通过左旋或右旋使之调整到树根,这样如果下次再访问这个元素的时候,访问操作就是O(1)了。如果我们要将当前节点移动到根,而不改变二叉排序树的特性。这样我们用这种数据结构后,算法就变成:将数据插入伸展树中并将数据调整到根,从根去找左子树的最右节点数据和右子树的最左节点数据,进行差的绝对值比较取小,并将所有的差的绝对值累加即可。splay,用到了查找树内某结点的前驱,后继和插入操作。

与上篇一样风格的代码:

#include <cstdio>
#define keyTree (ch[ ch[root][1] ][0])
#define inf 0x3f3f3f3f
const int maxn = 222222;
int ans;
struct SplayTree{
	int ch[maxn][2];
	int pre[maxn];
	int root , top1;
    /*这是题目特定变量*/
	int num[maxn];
	int val[maxn];
	inline void Rotate(int x,int f) {
		int y = pre[x];
		ch[y][!f] = ch[x][f];
		pre[ ch[x][f] ] = y;
		pre[x] = pre[y];
		if(pre[x]) ch[ pre[y] ][ ch[pre[y]][1] == y ] = x;
		ch[x][f] = y;
		pre[y] = x;
	}
	inline void splay(int x,int goal) {
		while(pre[x] != goal) {
			if(pre[pre[x]] == goal) {
				Rotate(x , ch[pre[x]][0] == x);
			} else {
				int y = pre[x] , z = pre[y];
				int f = (ch[z][0] == y);
				if(ch[y][f] == x) {
					Rotate(x , !f) , Rotate(x , f);
				} else {
					Rotate(y , f) , Rotate(x , f);
				}
			}
		}
		if(goal == 0) root = x;
	}
	//以下是题目的特定函数:
	inline void NewNode(int &x,int fa,int c) {
		x = ++top1;
		ch[x][0] = ch[x][1]= 0;
		pre[x]=fa;
        val[x] = c;/*这是题目特定函数*/
	}

	/*初始化*/
	inline int pre1(int x)
{
    int tmp=ch[x][0];
    if(tmp==0)
    return inf;
    while(ch[tmp][1])
    {
        tmp=ch[tmp][1];
    }
    return val[x]-val[tmp];
}
inline int suc(int x)
{
    int tmp=ch[x][1];
    if(tmp==0)
        return inf;
    while(ch[tmp][0])
        tmp=ch[tmp][0];
    return val[tmp]-val[x];
}
inline int ins(int data)
{
    int x=root;
    while(ch[x][val[x]<data])
    {
        if(val[x]==data)
        {
            splay(x,0);
            return 0;
        }
        x=ch[x][val[x]<data];
    }
    NewNode(ch[x][val[x]<data],x,data);
    splay(ch[x][val[x]<data],0);
    return 1;
}
inline void init(int n) {/*这是题目特定函数*/
	    int tmp;
		ch[0][0] = ch[0][1] = pre[0] =0;
		root = top1 = 0;ans=0;int a,b;
		//为了方便处理边界,加两个边界顶点
        for (int i = 0 ; i < n ; i ++)
        {
            scanf("%d",&tmp);
            if(i==0)
            {
                NewNode(root,0,tmp);
                ans+=tmp;continue;
            }
            if(ins(tmp)==0)
            continue;
            a=pre1(root);
            b=suc(root);
            ans+=a>b?b:a;
        }


	}
}spt;
int main() {
	int n , m;
	while(scanf("%d",&n)!=EOF) 
	{ spt.init(n);
	printf("%d\n",ans);
         }    
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值