这道题也提供两种做法
平衡树(略)
虽然说起来有点大材小用但是平衡树实在是太万能了
插入,全局第
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;
}
说实话我还挺喜欢这种把多个或者多种数据结构结合在一起的结合数据结构的……