题意:
给出 N 个数 , M 次查询。每次查询 L R 区间中第 K 大的数。
思路:
线段树+二分区间数列
转自:http://blog.sina.com.cn/s/blog_a46ca3520101be63.html
题目意思非常很简单。给十万个数,每次询问一段连续区间的第k大值。询问次数达到5000次。 在线段树中搜索出来的题目,但是很明显,区间需要记录什么信息才可以查询第k大值就很不好想。 最后想到,只有把这一段中的每个数的大小编号记录才能满足要求,那么就是把这一段中的每个数排序,那么存储的空间就变成了nlogn,构树的时间复杂度也是nlogn,线段树支持一个查询。一段中小于等于tmp的数的个数,用二分来完成这个查询。 最后找一段中的第k小数,二分枚举答案查询即可。 复杂度分析,查询的复杂度,一次二分枚举答案,查询时二分区间,找到区间时,二分数列,三次二分,复杂度为m*loginf*log100000*log100000 大概在5000万左右的复杂度,建树的复杂度大概在200万,所以总的时间复杂度在5000万左右。20s的题目,这个复杂度是可以满足的。
注意:
原文作者提供的思路是:在找到区间时,求一段中小于等于tmp的数的个数,是用二分来完成的,所以不会TLE。
#include <iostream> #include <stdio.h> #include <string.h> #define ls t<<1 #define rs t<<1|1 #define midt (tr[t].l+tr[t].r)>>1 using namespace std; const int maxn=111111; int a[maxn]; int d[maxn*20],lon; const int inf=1111111111; struct { int l,r; int ll,rr; }tr[maxn*4]; int maketree(int t,int l,int r) { tr[t].l=l; tr[t].r=r; if(l==r) { d[++lon]=a[l]; tr[t].ll=lon; tr[t].rr=lon; return(0); } int mid=midt; maketree(ls,l,mid); maketree(rs,mid+1,r); tr[t].ll=lon+1; int l1=tr[ls].ll,r1=tr[ls].rr; int l2=tr[rs].ll,r2=tr[rs].rr; while(l1<=r1&&l2<=r2) { if(d[l1]<d[l2]) d[++lon]=d[l1++]; else d[++lon]=d[l2++]; } while(l1<=r1) d[++lon]=d[l1++]; while(l2<=r2) d[++lon]=d[l2++]; tr[t].rr=lon; } int query(int l,int r,int tmp) { if(tmp>=d[r]) return(r-l+1); if(tmp<d[l]) return(0); int ll=l,rr=r; while(ll<rr) { int mid=(ll+rr)>>1; if(d[mid]>tmp) rr=mid; else ll=mid+1; } return(ll-l); } int query(int t,int l,int r,int tmp) { if(l<=tr[t].l&&r>=tr[t].r) { return(query(tr[t].ll,tr[t].rr,tmp)); } int mid=midt,ret=0; if(l<=mid) ret+=query(ls,l,r,tmp); if(mid+1<=r) ret+=query(rs,l,r,tmp); return(ret); } int main() { int n,k; scanf("%d %d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&a[i]); maketree(1,1,n); int l,r,tmp; for(int i=1;i<=k;i++) { int l,r,tmp; scanf("%d %d %d",&l,&r,&tmp); int sm=-inf,bg=inf; while(sm<bg) { int mid=(sm+bg)>>1; int t1=query(1,l,r,mid); if(t1<tmp) sm=mid+1; else bg=mid; } printf("%d\n",sm); } return 0; } 在线主席树: #include <iostream> #include <cstdio> #include <vector> #include <algorithm> #include <cstring> using namespace std; #define ls l,mid #define rs mid+1,r #define mi (l+r)/2 const int MAXN=1e5+7; vector <int> a; typedef struct Node{ int val; int son[2]; }Node; Node tree[MAXN*20]; int b[MAXN]; int cnt,pos,rtid[MAXN],ans; int new_node(){ int rt=++cnt; tree[rt].val=0; tree[rt].son[0]=tree[rt].son[1]=0; return rt; } int build(int l,int r){ int rt=new_node(); if(l==r){ return rt; } int mid=mi; tree[rt].son[0]=build(ls); tree[rt].son[1]=build(rs); return rt; } void update(int l,int r,int rt,int lart){ tree[rt].val=tree[lart].val; tree[rt].val++; if(l==r) return ; int mid=mi; if(pos>mid){ tree[rt].son[0]=tree[lart].son[0]; tree[rt].son[1]=new_node(); update(mid+1,r,tree[rt].son[1],tree[lart].son[1]); }else{ tree[rt].son[1]=tree[lart].son[1]; tree[rt].son[0]=new_node(); update(l,mid,tree[rt].son[0],tree[lart].son[0]); } } int query(int l,int r,int rt,int lart,int k){ if(l==r) return l; int mid=mi; int sum=tree[tree[rt].son[0]].val-tree[tree[lart].son[0]].val; if(sum>=k) return query(ls,tree[rt].son[0],tree[lart].son[0],k); else return query(rs,tree[rt].son[1],tree[lart].son[1],k-sum); } void ini(){ a.clear();cnt=0; } int main() { int n,m,x,st,en,k,T; while(scanf("%d%d",&n,&m)!=-1){ ini(); for(int i=1;i<=n;i++) scanf("%d",&b[i]),a.push_back(b[i]); sort(a.begin(),a.end());a.erase(unique(a.begin(),a.end()),a.end()); int len=a.size(); rtid[0]=build(1,len); for(int i=1;i<=n;i++){ pos=lower_bound(a.begin(),a.end(),b[i])-a.begin()+1; rtid[i]=++cnt; update(1,len,rtid[i],rtid[i-1]); } for(int i=1;i<=m;i++){ scanf("%d%d%d",&st,&en,&k); ans=query(1,len,rtid[en],rtid[st-1],k); printf("%d\n",a[ans-1]); } } }
Splay:
并归树:
由线段树改编
利用STL给出的 merge 每次并归即可 时间 O(nlognlognlogn)空间 O(nlogn)
#include <iostream> #include <vector> #include <cstdio> #include <algorithm> using namespace std; #define ls l,mid,rt*2 #define rs mid+1,r,rt*2+1 #define sf l,r,rt #define mi (l+r)/2 const int MAXN=1e5+100; const int INF=0x3f3f3f3f; vector <int> tree[MAXN*4],v; int x,n,m,ppp; int a[MAXN]; int st,en,k; int getid(int x){return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}//................多加了一步离散化,实际上不需要。 void push_up(int l,int r,int rt){ tree[rt].resize(r-l+1); merge(tree[rt*2].begin(),tree[rt*2].end(),tree[rt*2+1].begin(),tree[rt*2+1].end(),tree[rt].begin());//.....STL } void build(int l,int r,int rt){ if(l==r){tree[rt].clear();tree[rt].push_back(getid(a[++ppp]));return ;} int mid=mi; build(ls); build(rs); push_up(sf); return ; } int queryK(int l,int r,int rt,int v){ if(r<st||l>en) return 0; if(st<=l&&r<=en){ return upper_bound(tree[rt].begin(),tree[rt].end(),v)-tree[rt].begin(); } int mid=mi; int ans=0; ans+=queryK(ls,v); ans+=queryK(rs,v); return ans; } void solve(){//....................二分枚举,虽然会枚举到区间中不存在的数,但枚举结束后一定会枚举到正确的数 int l=0; int r=n; int mid; while(l<r){ mid=mi; if(queryK(1,n,1,mid)>=k){ r=mid; }else{ l=mid+1; } } printf("%d\n",v[l-1]); } int main() { scanf("%d%d",&n,&m); for(int i=0;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()); ppp=-1;build(1,n,1); while(m--){ scanf("%d%d%d",&st,&en,&k); solve(); } }划分树: