HNOI2002 - 营业额统计

本文介绍了一种使用二分搜索树(BST)实现的高效算法,该算法能够找出比给定数值小的最大值和大的最小值,进而求得两者之间的最小差值。通过构建特殊的二分搜索树节点结构,文章详细解释了如何进行插入操作、查找最邻近值,并展示了完整的C++实现代码。

找出比num小的最大数和比num大的最小数,然后计算差值和最小。只是两个查找的过程,和插入的过程,结果在控制a, b的位置wa了N次,

/*************************************************************************
    > File Name: HNOI2002.cpp
    > Author: Ac_Toy
    > Mail: ycsgldy@163.com 
    > Created Time: 2013年06月21日 星期五 06时48分17秒
 ************************************************************************/

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <climits>
#include <cstdio>
#include <string>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>

using namespace std;
typedef unsigned int u32;
typedef long long i64;
typedef unsigned long long u64;
typedef vector<int> IV;
typedef vector<bool> BV;
typedef pair<int,int> II;
typedef vector<II> IIV;
#define For(t,v,c) for(t::const_iterator v=c.begin(); v!=c.end(); ++v)
const int INF = 0x7FFFFFFF;
const double eps = 1E-10;
const double PI = acos(-1);
struct Node *null;
int cnt;//序列元素个数
struct Node {
	Node *ch[2];
	int s;//结点总数
	i64 v;
	int cmp(int k) const {
		int d = k - ch[0]->s;
		if ( d == 1 ) return -1;
		return d <= 0 ? 0 : 1;
	}
	void maintain() {
		s = ch[0]->s + ch[1]->s + 1;
	}
};
void rotate(Node* &o, int d) {//旋转
  Node* k = o->ch[d^1]; o->ch[d^1] = k->ch[d]; k->ch[d] = o;
  o->maintain(); k->maintain(); o = k; 
}

void splay(Node* &o, int k) {//找到序列的左数第k个元素并伸展到根
  int d = o->cmp(k);
  if(d == 1) k -= o->ch[0]->s + 1;
  if(d != -1) {
    Node* p = o->ch[d];
    int d2 = p->cmp(k);
    int k2 = (d2 == 0 ? k : k - p->ch[0]->s - 1);
    if(d2 != -1) {
      splay(p->ch[d2], k2);
      if(d == d2) rotate(o, d^1); else rotate(o->ch[d], d);
    }
    rotate(o, d^1);
  }
}

// 合并left和right。假定left的所有元素比right小。注意right可以是null,但left不可以
Node* merge(Node* left, Node* right) {
  splay(left, left->s);
  left->ch[1] = right;
  left->maintain();
  return left;
}

// 把o的前k小结点放在left里,其他的放在right里。1<=k<=o->s。当k=o->s时,right=null
void split(Node* o, int k, Node* &left, Node* &right) {
  splay(o, k);
  left = o;
  right = o->ch[1];
  o->ch[1] = null;
  left->maintain();
}
const int maxn = 100000 + 10;
int A[1];
struct SplaySequence {
	int n;
	Node seq[maxn];
	Node *root;

	Node* build(int sz) {
		if(!sz) return null;
		Node* L = build(sz/2);
		Node* o = &seq[++n];
		o->v = A[n-1];//结点编号;A[0]是虚拟结点
		o->ch[0] = L;
		o->ch[1] = build(sz - sz/2 - 1);
		o->s = 0;
		o->maintain();
		return o;
	}
	void init(int sz) {
		n = 0;
		null = new Node();
		null->s	= 0, null->v = INF;
		root = build(sz);
	}
};
vector<int> ans;
void debug(Node* o) {
  if(o != null) {
    debug(o->ch[0]);
    printf ( "%d ", o->v );
		//printf ( "  v=%d,s=%d,flip=%d,lazy=%d,mi=%d", o->v,o->s,o->flip,o->lazy2,o->mi);
		//printf ( "%d, left=%d,s=%d,mi=%d,*right=%d,s=%d,mi=%d\n", o->v, o->ch[0]->v,o->ch[0]->s,o->ch[0]->mi, o->ch[1]->v,o->ch[1]->s,o->ch[1]->mi );
    debug(o->ch[1]);
  }
}
SplaySequence ss;
int find_low(int x) {
	Node *p = ss.root, *ret = null;
	while(p != null) {
		if(p->v <= x) ret = p, p = p->ch[1];
		else p = p->ch[0];
	}
	return ret->v;
}
int find_up(int x) {
	Node *p = ss.root, *ret = null;
	while(p != null) {
		if(p->v >= x) ret = p, p = p->ch[0];
		else p = p->ch[1];
	}
	return ret->v;
}
void add(int x) {
	Node *p = ss.root;
	int k = 0;
	while(p != null) {
		if(p->v > x) p = p->ch[0];
		else {
			k += p->ch[0]->s + 1;
			p = p->ch[1];
		}
	}
	Node *left, *right;
	split(ss.root, k, left, right);
	Node *mid = new Node();
	mid->ch[0] = mid->ch[1] = null;
 	mid->s = 1; mid->v = x; 
	ss.root = merge(merge(left, mid), right);
}
int main()
{
	int Case, num, ans(0);
	scanf("%d", &Case);
	A[0] = -INF;
  ss.init(1);
	for(int i = 1; i <= Case; ++i) {
		if(scanf("%d",&num) == EOF) num = 0;
		int tmp = INF;
		int a = find_low(num), b = find_up(num);
		if(a != -INF) tmp = min(tmp, num - a);
		if(b != INF) tmp = min(tmp, b - num);
		if(tmp == INF) tmp = num;
		
		ans += tmp;
		if(tmp != 0) add(num);
	}	
	printf("%d\n", ans);
  return 0;
}


# P2234 [HNOI2002] 营业额统计 ## 题目描述 Tiger 最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况。 Tiger 拿出了公司的账本,账本上记录了公司成立以来每天的营业额。分析营业情况是一项相当复杂的工作。由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题。经济管理学上定义了一种最小波动值来衡量这种情况:当最小波动值越大时,就说明营业情况越不稳定。 而分析整个公司的从成立到现在营业情况是否稳定,只需要把每一天的最小波动值加起来就可以了。你的任务就是编写一个程序帮助 Tiger 来计算这一个值。 我们定义,一天的最小波动值 = $\min\{|\text{该天以前某一天的营业额}-\text{该天营业额}|\}$。 特别地,第一天的最小波动值为第一天的营业额。 ## 输入格式 第一行为正整数 $n$($n \leq 32767$) ,表示该公司从成立一直到现在的天数,接下来的 $n$ 行每行有一个整数 $a_i$($|a_i| \leq 10^6$) ,表示第 $i$ 天公司的营业额,可能存在负数。 ## 输出格式 输出一个整数,即每一天最小波动值的和,保证结果小于 $2^{31}$。 ## 输入输出样例 #1 ### 输入 #1 ``` 6 5 1 2 5 4 6 ``` ### 输出 #1 ``` 12 ``` ## 说明/提示 结果说明:$5+|1-5|+|2-1|+|5-5|+|4-5|+|6-5|=5+4+1+0+1+1=12$ c++
最新发布
12-27
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值