【Splay】BZOJ 1588 [HNOI2002]营业额统计

用splay的基本操作Find_pre和Find_succ来实现
</pre><pre name="code" class="cpp">
#include<cstdio>
#include<iostream>
#include<cmath>

using namespace std;
const int maxint = 214748364;
const int MAX_N = 40000;
//0表示空结点NULL

struct Node
{
	int key,num,fa;
	int son[2];
}Tree[MAX_N];
int root=0,tot=0;
int n;

//c=0 为左旋,c=1 为右旋
void Rotate(int x,int c){
	int y = Tree[x].fa;
	Tree[y].son[!c] = Tree[x].son[c];
	if(Tree[x].son[c]) Tree[Tree[x].son[c]].fa = y;
	Tree[x].fa = Tree[y].fa;
	if(Tree[y].fa)
	{
		/*if(Tree[y].fa.son[0] == y)
			Tree[y].fa.son[0] = x;
		else Tree[y].fa.son[1] = x;*/
		Tree[Tree[y].fa].son[Tree[Tree[y].fa].son[1]==y] = x;
	}
	Tree[y].fa = x;
	Tree[x].son[c] = y;
}

void Splay(int x, int f){

	while(Tree[x].fa != f)
	{
		if(Tree[Tree[x].fa].fa == f)
			Rotate(x,Tree[Tree[x].fa].son[0]==x);
		else
		{
			int y = Tree[x].fa;
			int kind = Tree[Tree[y].fa].son[1] == y; //kind=0 表示y为其父亲的左儿子,kind=1 表示y为右儿子
			if(Tree[y].son[kind] == x)
				Rotate(y,!kind) , Rotate(x,!kind);
			else
				Rotate(x,kind)  , Rotate(x,!kind);
		}
	}
	if(f==0) root = x;
}



void Insert(int val,int x){
	//根节点为0,即树为空
	if(!x){
		root = ++tot;
		Tree[tot].key = val;Tree[tot].fa = 0;Tree[tot].num = 1;
		return ;
	}
	if(Tree[x].key == val)
	{
		Tree[x].num++;
		Splay(x,0);//将当前结点旋转到NULL下,即成为根节点
		return;
	}
	//要插入的结点为空
	if(!Tree[x].son[val>Tree[x].key]){
		Tree[x].son[val>Tree[x].key] = ++tot;
		Tree[tot].fa = x;Tree[tot].key = val; Tree[tot].num = 1;
		Splay(tot,0);
	}
	else Insert(val,Tree[x].son[val>Tree[x].key]);
}

int Find_pre(int x){
	if(Tree[x].num>1) return Tree[x].key;
	int Next = Tree[x].son[0];
	if(!Next) return maxint; //没有前驱
	while(Tree[Next].son[1]) Next = Tree[Next].son[1];
	return Tree[Next].key;
}

int Find_succ(int x){
	if(Tree[x].num>1) return Tree[x].key;
	int Next=Tree[x].son[1];
	if(!Next) return maxint;//没有后继
	while(Tree[Next].son[0]) Next = Tree[Next].son[0];
	return Tree[Next].key;
}

void middle_order(int x)
{
    if(Tree[x].son[0])middle_order(Tree[x].son[0]);
    for(int i=1;i<=Tree[x].num;i++)printf("%d ",Tree[x].key);
    if(Tree[x].son[1])middle_order(Tree[x].son[1]);
}
int main()
{
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	scanf("%d",&n);
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		int val=0;
		scanf("%d",&val);
		Insert(val,root);
		if(i==1)
		{
			ans+=val;
			continue;
		}
		int Pred = Find_pre(root), Succ = Find_succ(root);
		ans += min(fabs(Pred-val), fabs(Succ-val));
	}
	printf("%d\n",ans);
	return 0;
}

或者使用c++的STL中的multiset来实现 这里发现如果先lower_bound来找答案的话会超时,而改为先插入当前元素,再使用find来计算答案就不会超时

/*happywu
 * 2014.8.12
 * bzoj 1588
 */
#include<cstdio>
#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
multiset<int> q;
multiset<int> ::iterator it;
int n;
int x;
/*int main()//超时
{
	freopen("a.in","r",stdin);
	scanf("%d",&n);
	multiset<long long >::iterator tmp;
	long long  ans=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&x);
		if(i==1){
            ans=x;
			q.insert(x);
            continue;
		}
		multiset<long long >::iterator it=lower_bound(q.begin(),q.end(),x);
		if(it==q.end())
		{
			it--;
			ans+=x-*it;
			it++;
		}
		else
		{
			if(it!=q.begin())
			{
				long long  k1=*it-x;
				it--;
				long long  k2=x-*it;
				ans+=min(k1,k2);
			}
			else
			{
				ans+=*it-x;
			}
		}
		q.insert(x);
	}
	printf("%lld\n",ans);
	return 0;
}*/
int main()
{
	//freopen("a.in","r",stdin);
	scanf("%d",&n);
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		if(scanf("%d",&x)==EOF)x=0; //输入数据有问题
		q.insert(x);
		if(i==1) ans+=x;
		else{
			it=q.find(x);
			int b=1<<30;
			if(it!=q.begin()) it--,b=x-(*it),it++;
			it++;
			if(it!=q.end()) b=min(b,(*it)-x);
			ans+=b;
		}
	}
	printf("%d\n",ans);
	return 0;
}

此题还可以用双向链表来实现 , 具体可见《基本数据结构在信息学竞赛中的应用》

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=1<<31-1;
const int maxn=1000000+10;
struct Node
{
	int key,idx;
	bool operator<(const Node&b)
	{
		if(key<b.key)return 1;
		return 0;
	}
}a[maxn];
int c[maxn],pre[maxn],nex[maxn];
bool cmp(const Node&a,const Node&b)
{
    if(a.key<b.key)return 1;
    return 0;
}
inline void Erase(int i)
{
	nex[pre[c[i]]]=nex[c[i]];
	pre[nex[c[i]]]=pre[c[i]];
}
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i].key);
		a[i].idx=i;
	}
	a[0].key=a[n+1].key=INF;
	a[0].idx=INF;a[n+1].idx=n+1;
	sort(a+1,a+n+1,cmp);
	for(int i=0;i<=n+1;i++)
		c[a[i].idx]=i; 
	for(int i=1;i<=n;i++)
	{
		nex[i]=i+1;
		pre[i]=i-1;
	}
	int ans=0;
	for(int i=n;i>=2;i--)
	{
		ans+=min(abs(a[nex[c[i]]].key-a[c[i]].key),abs(a[pre[c[i]]].key-a[c[i]].key));
		Erase(i);
	}
	printf("%d\n",ans+a[c[1]].key);
	return 0;
}

下面同样是双向链表,更加的简洁

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=1<<31-1;
const int maxn=1000000+10;
int a[maxn],id[maxn],pre[maxn],nex[maxn];
bool cmp(int a,int b)
{
	return ::a[a]< ::a[b];  //必须要在前面加上 ::
}
inline void Erase(int x)
{
	nex[pre[x]]=nex[x];
	pre[nex[x]]=pre[x];
}
inline int read()
{
	static int r,sign;
	static char c;
	r=0,sign=1;
	do {if((c=getchar())==EOF)return 0;}while(c!='-'&&(c<'0'||c>'9'));
	if(c=='-')sign=-1,c=getchar();
	while(c>='0' && c<='9') r=r*10+(int)(c-'0'),c=getchar();
	return sign*r;
}
int main()
{
	int n;
n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	a[0]=a[n+1]=INF;
	for(int i=1;i<=n;i++)id[i]=i;
	sort(id+1,id+n+1,cmp);
	for(int i=1;i<=n;i++)nex[id[i]]=id[i+1],pre[id[i]]=id[i-1];
	int ans=0;
	for(int i=n;i>=2;i--)
	{
		ans+=min(abs(a[nex[i]]-a[i]),abs(a[pre[i]]-a[i]));
		Erase(i);
	}
	printf("%d\n",ans+a[1]);
	return 0;
}


 
 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值