POJ 2104 K-th Number 主席树(求区间第k大)

来源:互联网 发布:大数据未来发展 编辑:程序博客网 时间:2024/05/29 05:55

点击打开链接

主席书资料

题意:给出n个数,m次询问,[x,y]内第k小的数时多少?n<=1e5,m<=5000

主席树:对原序列的每个前缀i都建立一个线段树 维护值域[l,r]中的每个数,在前缀i的出现次数.

此时l,r不在表示为下标,l,r为第l~r小的数

先将原序列离散化后,更新出主席树

求[x,y]内第k大的数?
若第1~mid大的数在[x,y]中出现的次数sum>=k,则[x,y]的第k大就在左子树中,否则[x,y]第k大为右子树的第k-sum个
由于主席树保存了第l~r大的数在任意前缀y和x-1的的出现次数,则第l~mid大的数在区间[x,y]的出现次数为T[T[y].l].sum-T[T[x-1].l].sum

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <vector>using namespace std;const int N=2e5+20;int n,m,a[N],x,y,k;int root[N],cnt;struct node{int l,r,sum; //此时l,r不在表示下标,sum表示第l~r大的数的出现次数}T[N*40];vector<int> v;int getid(int x)//离散化 {return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}int build(int l,int r){int rt=++cnt;T[rt].sum=0;T[rt].l=T[rt].r=0;if(l==r) return rt;int mid=(l+r)>>1;T[rt].l=build(l,mid);//l保存左子树结点号T[rt].r=build(mid+1,r); return rt;}void update(int l,int r,int &x,int y,int k)//根据第i个数是第几大来更新 {T[++cnt]=T[y],T[cnt].sum++,x=cnt;if(l==r) return;int mid=(l+r)>>1;if(mid>=k) update(l,mid,T[x].l,T[y].l,k);elseupdate(mid+1,r,T[x].r,T[y].r,k);}  int query(int l,int r,int x,int y,int k){if(l==r) return l; int mid=(l+r)>>1;int sum=T[T[y].l].sum-T[T[x].l].sum;if(sum>=k)return query(l,mid,T[x].l,T[y].l,k);elsereturn query(mid+1,r,T[x].r,T[y].r,k-sum); }int main(){while(cin>>n>>m){cnt=0;build(1,n);v.clear();for(int i=1;i<=n;i++)scanf("%d",&a[i]),v.push_back(a[i]);sort(v.begin(),v.end());v.erase(unique(v.begin(),v.end()),v.end()); for(int i=1;i<=n;i++)//前缀1~i和1~i-1只有一条链不同,那么root[i]其它结点只要用前一棵树的结点即可 update(1,n,root[i],root[i-1],getid(a[i]));// for(int i=1;i<=m;i++){scanf("%d%d%d",&x,&y,&k);int ans=query(1,n,root[x-1],root[y],k);printf("%d\n",v[ans-1]);//输出第k大离散化前的值 }}return 0;}


0 0
原创粉丝点击