洛谷 P1801 黑匣子_NOI导刊2010提高(06)

这道题也提供两种做法

平衡树(略)

虽然说起来有点大材小用但是平衡树实在是太万能了
插入,全局第 k k k大,最最最典型的两种操作

不多说了

#include<cstdio>
#include<set>
using namespace std;
int a[31000],b,cnt;
struct splay_tree
{
	struct node
	{
		int val,num,son[2],f,tot;
	}tr[31000];
	int len,root;
	
	#define xx tr[x]
	#define lc xx.son[0]
	#define rc xx.son[1]
	#define gf tr[f].f
	void update(int x)
	{xx.tot=tr[lc].tot+tr[rc].tot+xx.num;}
	
	void connect(int son,int fa,int dir)
	{
		tr[fa].son[dir]=son;
		if(son)tr[son].f=fa;
	}
	void add(int val,int x)
	{
		tr[++len]=(node){val,1,0,0,x,1};
		if(xx.val<val)xx.son[1]=len;
		else xx.son[0]=len;
	}
	void rotate(int x,int w)
	{
		int f=xx.f;
		connect(xx.son[w],f,1^w);
		connect(x,gf,f==tr[gf].son[1]);
		connect(f,x,w);
		update(f);update(x);
	}
	void splay(int x,int rt)
	{
		while(xx.f!=rt)
		{
			int f=xx.f;
			if(gf==rt)rotate(x,x==tr[f].son[0]);
			else
				if(f==tr[gf].son[0]&&x==tr[f].son[0]){rotate(f,1);rotate(x,1);}else
				if(f==tr[gf].son[1]&&x==tr[f].son[1]){rotate(f,0);rotate(x,0);}else
				if(f==tr[gf].son[1]&&x==tr[f].son[0]){rotate(x,1);rotate(x,0);}else
													 {rotate(x,0);rotate(x,1);}
		}
		if(rt==0)root=x;
	}
	int findpos(int val)
	{
		int x=root;
		while(xx.val!=val)
			if(val<xx.val)
				if(xx.son[0])x=xx.son[0];
				else break;
			else
				if(xx.son[1])x=xx.son[1];
				else break;
		return x;
	}
	
	void ins(int val)
	{
		if(!len){add(val,0);root=1;return;}
		int x=findpos(val);
		if(xx.val==val)
		{
			xx.num++;
			update(x);
			splay(x,0);
		}
		else
		{
			add(val,x);
			update(x);
			splay(len,0);
		}
	}
	int at(int rk)
	{
		int x=root;
		while(rk)
			if(rk<=tr[xx.son[0]].tot)
				x=xx.son[0];
			else if(rk<=tr[xx.son[0]].tot+xx.num)
				return xx.val;
			else
				rk-=tr[xx.son[0]].tot+xx.num,x=xx.son[1];
	}
}s;
int main()
{
	int n,m;scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",a+i);
	int last=1;
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&b);
		for(;last<=b;last++)
			s.ins(a[last]);
		printf("%d\n",s.at(i));
	}
	return 0;
}

要不是set不支持随机访问我就直接用set

对顶堆

当然平衡树还是不容易打的,而且打起来也不好调。
所以我们观察一下,这两种操作有没有什么特殊的性质。
其实显而易见:第 k k k大中的 k k k是一个接一个递增的。
对于这种变化范围不大的 k k k,我们可以选择对顶堆。

简介:对顶堆就是两个堆,其中一个为大顶堆,存比较小的一部分数,另外一个为小顶堆,存比较大的一部分数。通过对小顶堆或大顶堆中元素数的调整,我们可以获取整个数据结构中的第 k k k大/小数

就是对顶堆的元素压入和弹出有点细节要处理
因为我们不得不保证存放小元素的堆(代码中的small)中的所有元素都小于另一个堆(great)中的任意元素

#include<cstdio>
#include<functional>
using namespace std;
inline void swap(int &a,int &b)
{register int t=a;a=b,b=t;}
template<typename Cmp>
struct heap//模仿优先队列的堆,可以通过调整Cmp为greater<int>或less<int>来更改堆的属性
{
	int a[210000],len;
	Cmp cmp;
	
	void pushup(int i)
	{
		for(int x=i>>1;x&&cmp(a[x],a[i]);i=x,x=i>>1)
			swap(a[i],a[x]);
	}
	void pushdown(int x)
	{
		int i=x<<1;
		while(i<=len)
		{
			if(i<len&&cmp(a[i],a[i+1]))i++;
			if(cmp(a[x],a[i]))swap(a[x],a[i]);
			x=i;i=x<<1;
		}
	}
	
	int top()
	{return a[1];}
	int size(){return len;}
	void push(int x)
	{
		a[++len]=x;
		pushup(len);
	}
	void pop()
	{
		a[1]=a[len--];
		pushdown(1);
	}
};
heap<greater<int> >great;
heap<less<int> >small;
int a[210000],b;
int main()
{
	int n,m;scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",a+i);
	int last=1;
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&b);
		for(;last<=b;last++)
			if(small.size()&&a[last]<small.top())
			//如果这个数比small中最大的数还要小,我们就用它替换small的堆顶,同时把原堆顶放入great中,以保证small中的元素比great中的小
			{
				great.push(small.top());small.pop();
				small.push(a[last]);
			}
			else great.push(a[last]);
		//上面的if语句将small堆的大小维护在了i-1,但是我们要求出第i大
		small.push(great.top()),great.pop();
		printf("%d\n",small.top());
	}
	return 0;
}

说实话我还挺喜欢这种把多个或者多种数据结构结合在一起的结合数据结构的……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值