poj 2104 K-th Number(主席树,静态第k小)
2016-04-26 20:55
375 查看
题目链接:
http://poj.org/problem?id=2104
思路:
主席树。
根据定义,对于序列[1…n]的每一个前缀[1…i](i<=n)建立一颗线段树。然后每颗线段树的一个结点存的就是某个前缀[1...i]中区间为[l,r]上的数字出现的次数。从i-1到i的过程中,如果每次都建一棵树,肯定会MLE。但是由于我们处理的是前缀和,所以会发现从i-1到i的过程中其实只有一条路是不一样的,所以我们只要多开logn个结点,剩下的可以将指针指到历史的位置。
对于询问操作,因为我们我们求的是第k小,所以利用二分查找。如果在[1,n]的区间里面,可以先看左子树上的数字是否大于等于k,如果是就去左子树,否则就去右子树。那么到[l,r]的区间里面,其实就是T看[r]-T[l-1]的值,然后和k去比较大小。因为每颗线段树存的都是前缀和,所以符合前缀和的性质。
代码:
http://poj.org/problem?id=2104
思路:
主席树。
根据定义,对于序列[1…n]的每一个前缀[1…i](i<=n)建立一颗线段树。然后每颗线段树的一个结点存的就是某个前缀[1...i]中区间为[l,r]上的数字出现的次数。从i-1到i的过程中,如果每次都建一棵树,肯定会MLE。但是由于我们处理的是前缀和,所以会发现从i-1到i的过程中其实只有一条路是不一样的,所以我们只要多开logn个结点,剩下的可以将指针指到历史的位置。
对于询问操作,因为我们我们求的是第k小,所以利用二分查找。如果在[1,n]的区间里面,可以先看左子树上的数字是否大于等于k,如果是就去左子树,否则就去右子树。那么到[l,r]的区间里面,其实就是T看[r]-T[l-1]的值,然后和k去比较大小。因为每颗线段树存的都是前缀和,所以符合前缀和的性质。
代码:
#include<stdio.h> #include<string.h> #include<algorithm> #include<iostream> using namespace std; const int maxn = 100005; const int M = maxn * 30; int lson[M],rson[M],c[M],n,m,q,tot; int T[maxn],a[maxn],h[maxn],t[maxn]; void init_hash() { for(int i=1;i<=n;i++) { t[i]=a[i]; } sort(a+1,a+1+n); int siz=unique(a+1,a+1+n)-a-1; m=siz; } int hasher(int x) { int ans; return ans=lower_bound(a+1,a+1+m,x)-a; } int build(int l,int r) { int root=tot++; c[root]=0; if(l!=r) { int mid=l+r>>1; lson[root]=build(l,mid); rson[root]=build(mid+1,r); } return root; } int update(int root ,int pos,int val) { int newroot=tot++; int temp=newroot; int l=1,r=m; c[newroot]=c[root]+val; while(l<r) { int mid=l+r>>1; if(mid>=pos) { lson[newroot]=tot++; rson[newroot]=rson[root]; newroot=lson[newroot]; root=lson[root]; r=mid; } else { lson[newroot]=lson[root]; rson[newroot]=tot++; newroot=rson[newroot]; root=rson[root]; l=mid+1; } c[newroot]=c[root]+val; } return temp; } int query(int l_root,int r_root,int k) { int l=1,r=m; while(l<r) { int mid=l+r>>1; if(c[lson[r_root]]-c[lson[l_root]]>=k) { r=mid; r_root=lson[r_root]; l_root=lson[l_root]; } else { k-=(c[lson[r_root]]-c[lson[l_root]]); l=mid+1; r_root=rson[r_root]; l_root=rson[l_root]; } } return l; } int main() { int i,j,k,l,r,Ti; scanf("%d",&Ti); while(Ti--) { scanf("%d%d",&n,&q); tot=0; for(i=1;i<=n;i++) { scanf("%d",&a[i]); } init_hash(); T[0]=build(1,m); for(i=1;i<=n;i++) { int pos=hasher(t[i]); T[i]=update(T[i-1],pos,1); } //printf("%d %d %d %d\n",c[T[1]],c[T[2]],c[T[3]],c[T[4]]); while(q--) { scanf("%d%d%d",&l,&r,&k); printf("%d\n",a[query(T[l-1],T[r],k)]); } } }
相关文章推荐
- 让EditText无法点击编辑和RatingBar无法点击
- java中枚举类的使用详解
- #leetcode#21. Merge Two Sorted Lists
- 常见的解析方式(java)
- 我自己用的AHK脚本
- Tab切换
- 指针总结(—)
- 12. Integer to Roman
- 73种网页常用js代码(转)
- Caffe学习日记-Extracting Features
- Tab切换
- 冒泡排序法
- oracle中文数字转阿拉伯数字
- Jquery实现的Tabs标签页简洁版
- Python处理Excel(二):个性化Excel表格
- POJ 1681 Painter's Problem (高斯消元)
- 指针数组和数组指针
- beego api
- 13. Roman to Integer
- bat批处理