[POJ2104]K-th Number
思路1:
归并树+二分答案。
首先构建一棵归并树,然后二分查找最小的数$x$使得被查找区间中小于等于$x$的数的个数大于等于$k$。
注意vector中数字是从$0$开始存的,而询问是从$1$开始的,所以二分的时候要$-1$防止错位。
1 #include<iostream> 2 #include<algorithm> 3 #include<vector> 4 #define maxn 100000 5 #define root 1 6 #define _left <<1 7 #define _right <<1|1 8 using namespace std; 9 int n,m,i,j,k; 10 struct SegmentTree { 11 vector<int> val[maxn<<2]; 12 void push_up(const int p,const int b,const int e) { 13 val[p].resize(e-b+1); 14 merge(val[p _left].begin(),val[p _left].end(),val[p _right].begin(),val[p _right].end(),val[p].begin()); 15 } 16 void build(const int p,const int b,const int e) { 17 if(b==e) { 18 int t; 19 cin>>t; 20 val[p].push_back(t); 21 return; 22 } 23 int mid=(b+e)>>1; 24 build(p _left,b,mid); 25 build(p _right,mid+1,e); 26 push_up(p,b,e); 27 } 28 int query(const int p,const int b,const int e,const int l,const int r,const int x) { 29 if((b==l)&&(e==r)) { 30 return upper_bound(val[p].begin(),val[p].end(),x)-val[p].begin(); 31 } 32 else { 33 int mid=(b+e)>>1,ans=0; 34 if(l<=mid) ans+=query(p _left,b,mid,l,min(mid,r),x); 35 if(r>mid) ans+=query(p _right,mid+1,e,max(mid+1,l),r,x); 36 return ans; 37 } 38 } 39 void solve() { 40 int b=1,e=n; 41 while(b<e) { 42 int mid=(b+e)>>1; 43 if(query(root,1,n,i,j,val[root][mid-1])>=k) { 44 e=mid; 45 } 46 else { 47 b=mid+1; 48 } 49 } 50 cout<<val[root][e-1]<<endl; 51 } 52 }; 53 SegmentTree tree; 54 int main() { 55 ios_base::sync_with_stdio(false); 56 cin>>n>>m; 57 tree.build(root,1,n); 58 while(m--) { 59 cin>>i>>j>>k; 60 tree.solve(); 61 } 62 return 0; 63 }
思路2:
主席树。
建立一颗可持久化权值线段树,每次从$a$中取得一个数字时就将对应的数增加一个权值,并新增一条对应的链,那么每个节点上存储的数即位对应的数的权值前缀和。
查询时只要每次判断左边的的权值和是否$≥k$,如果是,则说明要查询的数在左边,否则在右边,递归查询即可。
1 #include<cstring> 2 #include<utility> 3 #include<iostream> 4 #include<algorithm> 5 const int N=100000,M=500001,SIZE=10000000; 6 class FotileTree { 7 private: 8 int val[SIZE],sz,left[SIZE],right[SIZE]; 9 int newnode() { 10 return sz++; 11 } 12 public: 13 int root[M]; 14 FotileTree() { 15 sz=0; 16 memset(val,0,sizeof val); 17 } 18 int build(const int b,const int e) { 19 int new_p=newnode(); 20 if(b==e) return new_p; 21 int mid=(b+e)>>1; 22 left[new_p]=build(b,mid); 23 right[new_p]=build(mid+1,e); 24 return new_p; 25 } 26 int modify(const int p,const int b,const int e,const int x) { 27 int new_p=newnode(); 28 val[new_p]=val[p]+1; 29 if(b==e) return new_p; 30 int mid=(b+e)>>1; 31 if(x<=mid) left[new_p]=modify(left[p],b,mid,x),right[new_p]=right[p]; 32 if(x>mid) right[new_p]=modify(right[p],mid+1,e,x),left[new_p]=left[p]; 33 return new_p; 34 } 35 int query(const int p1,const int p2,const int b,const int e,const int k) { 36 if(b==e) return b; 37 int mid=(b+e)>>1; 38 if(val[left[p2]]-val[left[p1]]>=k) return query(left[p1],left[p2],b,mid,k); 39 return query(right[p1],right[p2],mid+1,e,k-val[left[p2]]+val[left[p1]]); 40 } 41 }; 42 FotileTree t; 43 int main() { 44 std::ios_base::sync_with_stdio(false); 45 std::cin.tie(NULL); 46 int n,m; 47 std::cin>>n>>m; 48 t.root[0]=t.build(1,n); 49 std::pair<int,int> a[n]; 50 int hash[n],antihash[n]; 51 for(int i=0;i<n;i++) { 52 std::cin>>a[i].first; 53 a[i].second=i; 54 } 55 std::sort(&a[0],&a[n]); 56 for(int i=0;i<n;i++) { 57 hash[a[i].second]=i+1; 58 antihash[i]=a[i].first; 59 } 60 for(int i=1;i<=n;i++) t.root[i]=t.modify(t.root[i-1],1,n,hash[i-1]); 61 while(m--) { 62 int l,r,k; 63 std::cin>>l>>r>>k; 64 std::cout<<antihash[t.query(t.root[l-1],t.root[r],1,n,k)-1]<<std::endl; 65 } 66 return 0; 67 }
思路3:
树状数组+整体二分。
同时对询问的区间和答案进行分治,属于同一区间的询问可以同时分治,
用树状数组维护小于等于$mid$的数字个数$cnt$,
如果$cnt\leq{k}$则往小二分,如果$cnt>k$则往大二分。
以前也分别用归并树和主席树A过此题。
注意每个数的值可能是负数,写读优时要注意。(每次做这题都因为这个原因WA过)
1 #include<iostream> 2 #include<algorithm> 3 const int inf=0x7fffffff; 4 const int N=200001,M=5001; 5 struct Modify { 6 int id,val; 7 bool operator < (const Modify &x) const { 8 return val<x.val; 9 } 10 }; 11 Modify a[N]; 12 struct Query { 13 int a,b,k,cnt,id; 14 }; 15 Query q[M],s[M]; 16 int n; 17 class FenwickTree { 18 private: 19 int val[N]; 20 int lowbit(const int x) { 21 return x&-x; 22 } 23 public: 24 void modify(int p,const int x) { 25 while(p<=n) { 26 val[p]+=x; 27 p+=lowbit(p); 28 } 29 } 30 int query(int p) { 31 int ans=0; 32 while(p) { 33 ans+=val[p]; 34 p-=lowbit(p); 35 } 36 return ans; 37 } 38 }; 39 FenwickTree t; 40 int ans[M]; 41 void solve(int b,int e,int l,int r) { 42 if(b==e) { 43 for(int i=l;i<=r;i++) ans[q[i].id]=b; 44 return; 45 } 46 int ll=1,rr=n+1; 47 while(ll<rr) { 48 int mid=(ll+rr)>>1; 49 if(a[mid].val>=b) { 50 rr=mid; 51 } 52 else { 53 ll=mid+1; 54 } 55 } 56 int mid=(b+e)>>1; 57 for(int i=rr;i<=n&&a[i].val<=mid;i++) t.modify(a[i].id,1); 58 for(int i=l;i<=r;i++) q[i].cnt=t.query(q[i].b)-t.query(q[i].a-1); 59 for(int i=rr;i<=n&&a[i].val<=mid;i++) t.modify(a[i].id,-1); 60 int tmpl=l,tmpr=r; 61 for(int i=l;i<=r;i++) { 62 if(q[i].cnt>=q[i].k) { 63 s[tmpl++]=q[i]; 64 } 65 else { 66 q[i].k-=q[i].cnt; 67 s[tmpr--]=q[i]; 68 } 69 } 70 for(int i=l;i<=r;i++) q[i]=s[i]; 71 if(tmpl!=l) solve(b,mid,l,tmpl-1); 72 if(tmpr!=r) solve(mid+1,e,tmpr+1,r); 73 } 74 int main() { 75 std::ios_base::sync_with_stdio(false); 76 std::cin.tie(NULL); 77 int m; 78 std::cin>>n>>m; 79 int min=inf,max=-inf; 80 for(int i=1;i<=n;i++) { 81 a[i].id=i; 82 std::cin>>a[i].val; 83 min=std::min(min,a[i].val); 84 max=std::max(max,a[i].val); 85 } 86 std::sort(&a[1],&a[n+1]); 87 for(int i=1;i<=m;i++) { 88 std::cin>>q[i].a>>q[i].b>>q[i].k; 89 q[i].id=i; 90 } 91 solve(min,max,1,m); 92 for(int i=1;i<=m;i++) { 93 std::cout<<ans[i]<<std::endl; 94 } 95 return 0; 96 }