主席树或线段树-poj2104K-th Number
http://poj.org/problem?id=2104
我看来这两篇博客
http://blog.csdn.net/a2459956664/article/details/51302474
http://blog.sina.com.cn/s/blog_a46ca3520101be63.html
题目意思非常很简单。给十万个数,每次询问一段连续区间的第k大值。询问次数达到5000次。
在线段树中搜索出来的题目,但是很明显,区间需要记录什么信息才可以查询第k大值就很不好想。
最后想到,只有把这一段中的每个数的大小编号记录才能满足要求,那么就是把这一段中的每个数排序,那么存储的空间就变成了nlogn,构树的时间复杂度也是nlogn,线段树支持一个查询。一段中小于等于tmp的数的个数,用二分来完成这个查询。
最后找一段中的第k小数,二分枚举答案查询即可。
复杂度分析,查询的复杂度,一次二分枚举答案,查询时二分区间,找到区间时,二分数列,三次二分,复杂度为m*loginf*log100000*log100000 大概在5000万左右的复杂度,建树的复杂度大概在200万,所以总的时间复杂度在5000万左右。20s的题目,这个复杂度是可以满足的。
然后这个是我的代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define Ll long long
using namespace std;
struct tree{
int x,y,l,r;
}T[262144];
int a[100005],q[100000*18];
int n,m,x,y,z,ll;
void maketree(int x,int y,int z){
T[z].x=x; T[z].y=y;
if(x==y){
T[z].l=T[z].r=++ll;
q[ll]=a[x];
return;
}
maketree(x,(x+y)/2,z*2);
maketree((x+y)/2+1,y,z*2+1);
int X=z*2,Y=z*2+1;
T[z].l=ll+1;
T[z].r=ll+y-x+1;
int l=T[X].l,r=T[Y].l;
while(l<=T[X].r&&r<=T[Y].r)
if(q[l]>q[r])
q[++ll]=q[r++];
else
q[++ll]=q[l++];
while(l<=T[X].r)q[++ll]=q[l++];
while(r<=T[Y].r)q[++ll]=q[r++];
}
int er(int num,int z){
int l=T[num].l,r=T[num].r,ans=-1;
while(r>=l){
int mid=l+r>>1;
if(q[mid]<=z){
ans=max(ans,mid);
l=mid+1;
}else r=mid-1;
}
return max(ans-T[num].l+1,0);
}
int outit(int num,int x,int y,int z){
if(x<=T[num].x&&T[num].y<=y)
return er(num,z);
int ans=0;
num=num*2;
if(T[num].y>=x)ans+=outit(num,x,y,z);
if(T[num+1].x<=y)ans+=outit(num+1,x,y,z);
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
maketree(1,n,1);
while(m--){
scanf("%d%d%d",&x,&y,&z);
int l=-1e9,r=1e9;
while(r>l){
// cout<<l<<' '<<r<<endl;
int mid=l+r>>1;
if(outit(1,x,y,mid)>=z)r=mid;else l=mid+1;
}
printf("%d\n",l);
}
}
有两点强调一下;
我线段树的题目,关于线段树的数组要开多大?
假如下标为1~n;
设2^k刚好大于或等于n;
那么开成(2^(k+1))大小就好了;
因为线段树就是一个满二叉树;
还有一点我还没研究好;
关于主席树
http://blog.csdn.net/fop_zz/article/details/69240536
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define For(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
const int N=1e5*18;//n*log2(n)
int L[N],R[N],val[N],root[100001],a[100001],t[100001];
int n,m,tot,len;
inline void update(int aci,int &x,int l,int r,int v){
x=++tot;
val[x]=val[aci]+1;
L[x]=L[aci];
R[x]=R[aci];
if(l==r) return;
int mid=(l+r)>>1;
if(v<=mid) update(L[aci],L[x],l,mid,v);else update(R[aci],R[x],mid+1,r,v);
}
inline int query(int x,int y,int l,int r,int v){
if(l==r) return l;
int now=val[L[y]]-val[L[x]];
int mid=(l+r)>>1;
if(now>=v) return query(L[x],L[y],l,mid,v);else return query(R[x],R[y],mid+1,r,v-now);
}
int main(){
scanf("%d%d",&n,&m);
For(i,1,n) scanf("%d",&a[i]);
For(i,1,n) t[i]=a[i];
sort(t+1,t+n+1);
len=unique(t+1,t+n+1)-t-1;//判重
For(i,1,n) a[i]=lower_bound(t+1,t+len+1,a[i])-t,update(root[i-1],root[i],1,len,a[i]);
For(i,1,m){
int t1,t2,k;
scanf("%d%d%d",&t1,&t2,&k);
printf("%d\n",t[query(root[t1-1],root[t2],1,len,k)]);
}
}