POJ_2104_K-th Number_主席树
POJ_2104_K-th Number_主席树
题意:给定一个长度为n的序列,m次询问区间第k小
分析:
主席树模板
主席树可以理解成为n棵权值线段树的前缀和
但我们不能建n棵线段树,只需要对于每个修改的结点新建一个点,剩下的儿子什么的连到上一棵树的儿子上
这样做到节约空间,实际上我们只需要开nlogn的空间
对于这道题就相当于对每个前缀建一棵树
查询的时候前缀和相减,在相减后的线段树上查询
代码简短思路清晰
代码:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define N 100050 int t[N*20],n,m,a[N],root[N],tot,ls[N*20],rs[N*20]; struct A { int num,id,v; }d[N]; bool cmp1(const A &x,const A &y) { return x.num<y.num; } bool cmp2(const A &x,const A &y) { return x.id<y.id; } void insert(int x,int &y,int l,int r,int val) { y=++tot; if(l==r){ t[y] = t[x] + 1; return ; } int mid=l+r>>1; if(val<=mid) rs[y]=rs[x],insert(ls[x],ls[y],l,mid,val); else ls[y]=ls[x],insert(rs[x],rs[y],mid+1,r,val); t[y]=t[ls[y]]+t[rs[y]]; } int query(int x,int y,int l,int r,int k) { if(l==r)return a[l]; int sizls=t[ls[y]]-t[ls[x]],mid=l+r>>1; if(k<=sizls) return query(ls[x],ls[y],l,mid,k); else return query(rs[x],rs[y],mid+1,r,k-sizls); } int main() { scanf("%d%d",&n,&m); int i,x,y,k,j; for(i=1;i<=n;i++) scanf("%d",&d[i].num),d[i].id=i; sort(d+1,d+n+1,cmp1);d[0].num=453753322; for(i=1,j=0;i<=n;i++) { if(d[i].num!=d[i-1].num)j++;d[i].v=j;a[j]=d[i].num; } sort(d+1,d+n+1,cmp2); for(i=1;i<=n;i++) insert(root[i-1],root[i],1,n,d[i].v); for(i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&k); printf("%d\n",query(root[x-1],root[y],1,n,k)); } }