题解 P3871 【[TJOI2010]中位数】
前铺
虽然刚学普通平衡树,但刷了这么多题后,发现用平衡树解的题目方法就一个字:套!如何套呢?我总结了些步骤:
1、分析:看清题目找到题目所要求的的平衡树的哪个操作。
2、修改:在模板上加以修改。
今天我就拿这道题来实地演练一下。
PS:如果你还不会普通平衡树,请出门左转 luogu P3369。
分析
首先,题目说有 add 添加一个整数,那么显然我们需要插入操作。在看到 mid 的从小到大,我们肯定要进行排序,然后题目让我们求中位数,和为中位数?
中位数是指将一个序列按照从小到大排序后处在中间位置的数。(若序列长度为偶数,则指处在中间位置的两个数中较小的那个)
说白了在代码里就是第 (序列长度+1)/2 个数 。那我们就需要求出排名为第 (序列长度+1)/2 个数输出即可。
修改
对于板子来说并没有太多的修改只需保留插入、排序、查询第 x 大的数即可。
总结
本题如果用平衡树解那重点也就只有将中位数这个概念转化成第 (n+1)/2 个大的数。
完整代码
#include<stdio.h>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N=1e5+86+3e4;
int n,m,tot,root;//tot即为当前序列长度
struct Treap
{
Treap()
{
pri=rand()*rand()%20061124;
l=r=0;
}//构造函数初始化
int l;
int r;
int pri;
int v;
int siz;
}t[N];
void up(int x)//维护节点个数
{
t[x].siz=t[t[x].l].siz+t[t[x].r].siz+1;
}
void right_rorate(int &x)//右旋
{
int tmp=t[x].l;
t[x].l=t[tmp].r;
t[tmp].r=x;
t[tmp].siz=t[x].siz;
up(x);
x=tmp;
}
void left_rorate(int &x)//左旋
{
int tmp=t[x].r;
t[x].r=t[tmp].l;
t[tmp].l=x;
t[tmp].siz=t[x].siz;
up(x);
x=tmp;
}
int add(int v)//添加操作
{
t[++tot].v=v;
t[tot].siz=1;
return tot;
}
void insert(int &x,int v)//插入操作
{
if(x==0)
{
x=add(v);
return;
}
t[x].siz++;
if(v>=t[x].v)
insert(t[x].r,v);
else
insert(t[x].l,v);
if(t[x].l&&t[x].pri>t[t[x].l].pri)
right_rorate(x);
if(t[x].r&&t[x].pri>t[t[x].r].pri)
left_rorate(x);
up(x);
}
int find(int x,int rnk)//查询操作
{
if(rnk==t[t[x].r].siz+1)
return t[x].v;
if(rnk>t[t[x].r].siz+1)
return find(t[x].l,rnk-t[t[x].r].siz-1);
return find(t[x].r,rnk);
}
int main()
{
scanf("%d",&n);
int a;
char s[3];
while(n--)
{
scanf("%d",&a);
insert(root,a);
}
scanf("%d",&m);
while(m--)
{
scanf("%s",s);
if(s[0]=='a')
{
scanf("%d",&a);
insert(root,a);
}
else if(s[0]=='m')
printf("%d\n",find(root,(tot+1)/2));//转化中位数概念
}
return 0;
}
希望对大家有所帮助!