[BZOJ 1588] HNOI 2002 营业额统计

首先!特别强调!这题非常坑!比如第一个点!n=32767,但是只给了你32766个值,最后一个当成0来做!就这问题笔者调了一个小时才发现!而且网上的题解中都没有提到!请各位猿们在写代码时特别注意!!!

今天刚刚学了Splay,就拿这题练手了。

关于Splay,建议大家去看国家集训队论文2004年杨思雨的《伸展树的基本操作与应用》,笔者觉得很适合初学者看。

那么对于本题,目的是对于每个数找出之前的数中和它相差最小的数。

每读入一个数就插入树中,经过Splay操作后查找其前趋和后继并进行比较,计算答案。

讲义中虽然没有提到,但是直接Splay的话要判断是Zig还是Zag,但是Splay操作实际代码实现时非常简洁巧妙,希望各位猿们慢慢的看和领悟,拿着草稿纸自己画画写写,当你像笔者当时一样终于看懂时,你会发现这个方法实在是太巧妙了,平衡树往往一两百行的程序,但是这题笔者只写了70行。

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <iostream>
using namespace std;
#define read freopen("1588.in","r",stdin)
#define write freopen("1588.out","w",stdout)
#define inf (1<<30)

const int maxn=100005;
int n,k,pre[maxn],data[maxn],ch[maxn][2],tot,ans,root;

//kind==1 Zig  kind==0 Zag
void rotate(int x,int kind){				
	int y=pre[x];
	ch[y][!kind]=ch[x][kind];
	pre[ch[x][kind]]=y;
	if (pre[y])
	   ch[pre[y]][ch[pre[y]][1]==y]=x;
	pre[x]=pre[y];
	ch[x][kind]=y;
	pre[y]=x;
}

void splay(int t){
	while (pre[t]!=0)
		if (pre[pre[t]]==0) rotate(t,ch[pre[t]][0]==t);
		else{
			int y=pre[t];
			bool kind=ch[pre[y]][1]==y;
			if (ch[y][kind]==t)
			   rotate(t,!kind),rotate(t,!kind);
 		  	else 
			   rotate(t,kind),rotate(t,!kind);
	    }
   root=t;
}

void ins(int k){						//伸展树的查找、插入和查找树是一样的 
	int now;
	for (now=root;ch[now][data[now]<k];now=ch[now][data[now]<k]); 
	data[++tot]=k;					
	ch[now][data[now]<k]=tot;
	pre[tot]=now;
	splay(tot);	 
}

int get_pre(){
	int now=ch[root][0];
	if (!now) return inf;				//特判是否有左/右子树 
	for (;ch[now][1];now=ch[now][1]);
	return k-data[now];
}

int get_suf(){
	int now=ch[root][1];
	if (!now) return inf;				//同上 
	for (;ch[now][0];now=ch[now][0]);
	return data[now]-k;
}

int main(){
	#ifndef ONLINE_JUDGE
	read;write;
	#endif
	cin>>n;tot=0;
	for (int i=1;i<=n;i++){
		if (scanf("%d",&k)==EOF) k=0;
		ins(k);
		if (i==1) {ans+=k;continue;}
		ans+=min(get_pre(),get_suf());
	}
	cout<<ans<<endl;
	return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值