POJ - 2104 K-th Number
题目大意:给你n个数,q个询问,每个询问问你在 l 到 r 之间的第k个数是多大。
思路:很经典的一道题,有许多种做法。
第一种:在挑战程序设计里面有介绍的分桶法。
第二种:以建立一棵线段树,每个节点维护当前区间的有序数组。
第三种:刚学的主席树,一棵普通的线段树在进行修改后是无法保存以前的线段树的,主席树的
作用就是把线段树更新前后的版本都保留下来。对于l 到 r 第 k 大的, 我们只要比较r插入后的
线段树版本和 l 插进来之前的线段树版本,就能找到第k大的数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<iostream> 3 #include<vector> 4 #include<algorithm> 5 using namespace std; 6 const int N=1e5+5; 7 struct node 8 { 9 int l,r,sum; 10 } seg[N*40]; 11 vector<int> v; 12 int a[N],n,q,x,y,k,root[N],cnt;//root[i],表示插入第i数之后版本的树根。 13 int get_id(int x){ return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}//二分查离散后的序号 14 void update(int l,int r,int &x,int y,int pos)//注意x用引用,这样边进行递归,边给节点的父亲赋值。 15 { 16 seg[++cnt]=seg[y]; seg[cnt].sum++; x=cnt; 17 if(l==r) return; 18 int m=(l+r)>>1; 19 if(m>=pos) update(l,m,seg[x].l,seg[y].l,pos); 20 else update(m+1,r,seg[x].r,seg[y].r,pos); 21 } 22 int query(int l,int r,int x,int y,int k) 23 { 24 if(l==r) return l; 25 int res=0,m=(l+r)>>1; 26 res+=seg[seg[y].l].sum-seg[seg[x].l].sum;// 这是x和y 两个版本之间从l 到 m数量的差值。 27 if(res>=k) return query(l,m,seg[x].l,seg[y].l,k); //如果差值大于等于k,说明第k个数在l 到 m之间 28 else return query(m+1,r,seg[x].r,seg[y].r,k-res); 29 } 30 int main() 31 { 32 scanf("%d%d",&n,&q); 33 for(int i=1;i<=n;i++) scanf("%d",&a[i]),v.push_back(a[i]); 34 sort(v.begin(),v.end()); v.erase(unique(v.begin(),v.end()),v.end());//将数组里的数排序,去重进行离散化。 35 for(int i=1;i<=n;i++) update(1,n,root[i],root[i-1],get_id(a[i])); 36 for(int i=1;i<=q;i++) 37 { 38 scanf("%d%d%d",&x,&y,&k); 39 printf("%d\n",v[query(1,n,root[x-1],root[y],k)-1]); 40 } 41 return 0; 42 }
还有一种整体二分的写法,整体二分是一种离线算法,对于只有一个询问的k小,我们可以二分答案,有m个询问,
如果对每个都进行二分肯定会有冗余,整体二分就是进行一次二分答案,就能得出所有结果。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #define fi first 4 #define se second 5 #define pb push_back 6 #define mk make_pair 7 #define pii pair<int,int> 8 #define read(x) scanf("%d",&x) 9 #define lread(x) scanf("%lld",&x) 10 using namespace std; 11 12 typedef long long ll; 13 const int N=2e5+7; 14 const int mod=1e9+7; 15 const int base=17; 16 const int inf=0x3f3f3f3f; 17 const ll INF=0x3f3f3f3f3f3f3f3f; 18 19 int n,m,tot,ans[N]; 20 pii a[N]; 21 struct Qus 22 { 23 int l,r,k,cnt,id; 24 }qus[N],tmp[N]; 25 struct BIT 26 { 27 int a[N]; 28 void modify(int x,int v) 29 { 30 for(int i=x;i<=n;i+=i&-i) 31 a[i]+=v; 32 } 33 int sum(int x) 34 { 35 int ans=0; 36 for(int i=x;i;i-=i&-i) 37 ans+=a[i]; 38 return ans; 39 } 40 }bit; 41 void cal(int l,int r,int down,int mid) 42 { 43 int pos=lower_bound(a+1,a+1+n,mk(down,0))-a; 44 for(int i=pos;i<=n && a[i].fi<=mid;i++) 45 bit.modify(a[i].se,1); 46 for(int i=l;i<=r;i++) 47 qus[i].cnt=bit.sum(qus[i].r)-bit.sum(qus[i].l-1); 48 for(int i=pos;i<=n && a[i].fi<=mid;i++) 49 bit.modify(a[i].se,-1); 50 51 } 52 void work(int l,int r,int down,int up) 53 { 54 if(down>up || l>r) return; 55 if(down==up) 56 { 57 for(int i=l;i<=r;i++) 58 ans[qus[i].id]=down; 59 return; 60 } 61 int mid=(up+down)>>1; 62 int item1=l,item2=r; 63 cal(l,r,down,mid); 64 for(int i=l;i<=r;i++) 65 { 66 if(qus[i].cnt>=qus[i].k) 67 tmp[item1++]=qus[i]; 68 else 69 { 70 qus[i].k-=qus[i].cnt; 71 tmp[item2--]=qus[i]; 72 } 73 } 74 for(int i=l;i<=r;i++) 75 qus[i]=tmp[i]; 76 work(l,item1-1,down,mid); 77 work(item2+1,r,mid+1,up); 78 } 79 int main() 80 { 81 int mx=-inf,mn=inf; 82 read(n); read(m); 83 for(int i=1;i<=n;i++) 84 { 85 read(a[i].fi),a[i].se=i; 86 mx=max(mx,a[i].fi); 87 mn=min(mn,a[i].fi); 88 } 89 sort(a+1,a+n+1); 90 for(int i=1;i<=m;i++) 91 { 92 read(qus[i].l); 93 read(qus[i].r); 94 read(qus[i].k); 95 qus[i].id=i; 96 } 97 work(1,m,mn,mx); 98 for(int i=1;i<=m;i++) 99 printf("%d\n",ans[i]); 100 return 0; 101 } 102 /* 103 */