这几天考试考得心里颇不宁静,于是就来学习一下权值线段树,这是我打的第一道题~
题意:
有以下三种操作:
1:in a 把a放到队里
2:out 取出最先放入的元素
3:query 输出队中元素的中位数
#多组数据
#中位数:n个数字排好序第(n/2+1)个数
思路:
这道题网上许多神犇说是用的set,那么这里主要介绍权值线段树的解法。
首先,要搞清楚权值线段树和普通线段树的区别:权值线段树是根据元素权值排的(有时需离散化),而普通线段树是根据元素编号进行划分;
所以,看得出在权值线段树上找中位数是件很容易的事;
那么问题来了:如何维护一个动态区间的中位数呢?
1:我们记下所有的操作 pre[1~n],顺便存下所有元素(排好序)now[1~sum];
#sum:in的数量
2:接着,建树 tree[1~maxn<<2]
#tree[i] 存的是 tree[i] 这个区间中元素个数
2:然后,按照顺序执行操作:
in(pre[i]>=0):找到插入的数(pre[i])在树上的位置(lower_bound) tmp;
在树上tmp位置+1,表示tmp位置上有数;
push_up;
out(pre[i]==-1):找到队首(q.front())在树上的位置 tmp;
在树上tmp位置-1,表示tmp位置上的数没了;
push_up;
q.pop();
query(pre[i]==-2):寻找在队中间(q.size()/2+1)位置的元素在树上的位置 zz;
输出now[zz];
好了,废话不多说,接下来附上代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#define For(aa,bb,cc) for(int aa=bb;aa<=cc;++aa)
#define ls node<<1
#define rs node<<1|1
#define maxn 100005
using namespace std;
int now[maxn],pre[maxn],tree[maxn<<2];
int Case,n;
void push_up(int node){
tree[node]=tree[ls]+tree[rs];
}
void build(int node,int l,int r){
if(l==r){
tree[node]=0;
return ;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
push_up(node);
}
void updata(int node,int l,int r,int tmp,int flag){
if(l==r){
tree[node]+=flag;
return;
}
int mid=(l+r)>>1;
if(tmp<=mid) updata(ls,l,mid,tmp,flag);
else updata(rs,mid+1,r,tmp,flag);
push_up(node);
}
int query(int node,int l,int r,int tmp){
if(l==r) return l;
int mid=(l+r)>>1;
if(tree[ls]>=tmp) return query(ls,l,mid,tmp);
else return query(rs,mid+1,r,tmp-tree[ls]);
}
void work(){
++Case;
printf("Case #%d:\n",Case);
queue <int> q;
For(i,1,n){
char s[10];
scanf("%s",&s);
if(s[0]=='i') scanf("%d",&pre[i]);
else if(s[0]=='o') pre[i]=-1;
else pre[i]=-2;
}
int sum=0;
For(i,1,n){
if(pre[i]<0) continue;
now[++sum]=pre[i];
}
sort(now+1,now+sum+1);
build(1,1,sum);
For(i,1,n){
if(pre[i]>=0){
int tmp=lower_bound(now+1,now+sum+1,pre[i])-now;
updata(1,1,sum,tmp,1);
q.push(pre[i]);
}
else if(pre[i]==-1){
int ret=q.front();
int tmp=lower_bound(now+1,now+sum+1,ret)-now;
updata(1,1,sum,tmp,-1);
q.pop();
}
else{
int zz=query(1,1,sum,q.size()/2+1);
printf("%d\n",now[zz]);
}
}
}
int main(){
while(~scanf("%d",&n)) work();
return 0;
}
若想更直观的了解权值线段树,看这篇(权值线段树求逆序对)”:
http://blog.csdn.net/Monkey_king2017cn/article/details/60323601