题意:给出1e5个数,向你提5000问,L到R间的第K个数是多少?
————————————————————————————————————————————————————————
这个题着实没有其它很好的办法,是主席树的经典题目。
主席树,又叫可持久化线段树或者函数式线段树。我只知道这些了,据说是一位大神没学会划分树就自己搞了这么个替代品,结果,比原来的还要强。哎~,真不知道这群大神的脑子是咋整的,膜拜吧!
本人水平有限,这是我见过的最神奇的数据结构了,感觉比伸展树、熟练剖分神奇多了,鼓捣了大半天才明白其中的原理。
说一下思路:
因为是求第k大的数,所以线段树是不行的,因为数是排好序的,所以伸展树好像也不好解决。
所以主席树用到了前缀和的思想,如果知道每个数在L前出现了几次,R前出现了几次,做差就可以在L~R间出现了几次,自然也就知道k大了!
所以,第一步把所有的数排序、去重。
数列:2 4 6 8 8 6 4 2
hash:2 4 6 8
数列变为:1 2 3 4 4 3 2 1(就是各个数在hash中的排序)
第二部建n+1个线段树,就是按照数列的顺序每读入一个数就建一个线段树,把新树在上一颗树的基础上把这个数字按照去重后的排序加入当前线段树。
这样求L~R的k值就用线段树R减去线段树L-1,在差中找k大值就可以了。
......
出现的问题:超空间
这个事大神替我们想到了,那就是每一个线段树与上一个线段树只有一条路径(根到叶)不同,所以,新树除了这条路径外所有节点都用上一个线段树的。
这样空间为第一颗线段树(4*n)+每个数字一条链(n*logn)
代码敲了1天多,主要是本人习惯于用指针和new,但是这次点太多,超时了。没办法只好给出数组模拟指针。ok!
网上有人说可以一次性malloc多个,这样可以省时通过,没来得及试,不过应该可以,但是就没有了指针+new的灵活性,不做也罢。
但是没舍得把指针的丢掉,这个理解起来清楚容易,哎~,就这麽着吧!
————————————————————————————————————————————————————————
数组版:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 6 using namespace std; 7 const int maxn=100005; 8 const int maxnn=10000000; 9 int root[maxn],ls[maxnn],rs[maxnn],cnt[maxnn],tot; 10 int sz[maxn],hash[maxn]; 11 void build(int &cur,int l,int r) 12 { 13 cur=tot++; 14 cnt[cur]=0; 15 if(l!=r) 16 { 17 int mid=(l+r)/2; 18 build(ls[cur],l,mid); 19 build(rs[cur],mid+1,r); 20 } 21 } 22 void update(int pre,int ps,int &cur,int l,int r) 23 { 24 cur=tot++; 25 cnt[cur]=cnt[pre]+1; 26 ls[cur]=ls[pre];rs[cur]=rs[pre]; 27 if(l==r)return ; 28 int mid=(l+r)/2; 29 if(ps<=mid)update(ls[pre],ps,ls[cur],l,mid); 30 else update(rs[pre],ps,rs[cur],mid+1,r); 31 } 32 int query(int lt,int rt,int l,int r,int k) 33 { 34 if(l==r)return l; 35 int mid=(l+r)/2,cha=cnt[ls[rt]]-cnt[ls[lt]]; 36 if(k<=cha)return query(ls[lt],ls[rt],l,mid,k); 37 else return query(rs[lt],rs[rt],mid+1,r,k-cha); 38 } 39 int main() 40 { 41 int m,n,l,r,k; 42 while(scanf("%d%d",&n,&m)==2) 43 { 44 for(int i=1;i<=n;++i) 45 { 46 scanf("%d",sz+i); 47 hash[i]=sz[i]; 48 } 49 sort(hash+1,hash+n+1); 50 int siz=unique(hash+1,hash+1+n)-hash-1; 51 for(int i=1;i<=n;++i) 52 sz[i]=lower_bound(hash+1,hash+1+siz,sz[i])-hash; 53 tot=0; 54 build(root[0],1,siz); 55 for(int i=1;i<=n;++i) 56 update(root[i-1],sz[i],root[i],1,siz); 57 while(m--) 58 { 59 scanf("%d%d%d",&l,&r,&k); 60 printf("%d\n",hash[query(root[l-1],root[r],1,siz,k)]); 61 } 62 } 63 return 0; 64 }