Time Limit: 20000MS | Memory Limit: 65536K | |
Total Submissions: 43988 | Accepted: 14569 | |
Case Time Limit: 2000MS |
Description
Input
Output
Sample Input
7 3 1 5 2 6 3 7 4 2 5 3 4 4 1 1 7 3
Sample Output
5 6 3
Hint
Source
有没有觉得题目很熟悉啊?是不是感觉和poj2761很像啊?不过这道题稍微麻烦一点,区间之间存在包含关系,所以就不能用排序+平衡树做了。
这道题的正确解法是划分树(可用于求区间第k小数,复杂度log(n))。划分树是一种基于线段树的数据结构,基本思想是对于一个区间,将它划分成两个区间,左区间的数全部小于等于右区间的数,分别对应左右子树。构建划分树时要记录到达某个位置时进入左子树的数的个数num,查询时就可以通过num确定下一个查询区间,最后将区间范围缩小到1,也就找到了答案。(详见代码…)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<cmath> #define F(i,j,n) for(int i=j;i<=n;i++) #define D(i,j,n) for(int i=j;i>=n;i--) #define LL long long #define pa pair<int,int> #define MAXN 100005 using namespace std; int n,m,a[MAXN],val[20][MAXN],num[20][MAXN]; inline int read() { int ret=0,flag=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') flag=-1;ch=getchar();} while (ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();} return ret*flag; } inline void build(int l,int r,int x) { if (l==r) return; int mid=(l+r)>>1,lsame=mid-l+1,same=0,ln=l,rn=mid+1; F(i,l,r) if (val[x][i]<a[mid]) lsame--; F(i,l,r) { if (i==l) num[x][i]=0;else num[x][i]=num[x][i-1]; if (val[x][i]<a[mid]) num[x][i]++,val[x+1][ln++]=val[x][i]; else if (val[x][i]>a[mid]) val[x+1][rn++]=val[x][i]; else { if (lsame>=++same) num[x][i]++,val[x+1][ln++]=val[x][i]; else val[x+1][rn++]=val[x][i]; } } build(l,mid,x+1); build(mid+1,r,x+1); } inline int query(int st,int ed,int k,int l,int r,int x) { if (l==r) return val[x][l]; int lx,ly,rx,ry,mid=(l+r)>>1; if (st==l) lx=0,ly=num[x][ed]; else lx=num[x][st-1],ly=num[x][ed]-lx; if (ly>=k) { st=l+lx;ed=st+ly-1; return query(st,ed,k,l,mid,x+1); } else { rx=st-l-lx;ry=ed-st+1-ly; st=mid+1+rx;ed=st+ry-1; return query(st,ed,k-ly,mid+1,r,x+1); } } int main() { n=read();m=read(); F(i,1,n) val[0][i]=a[i]=read(); sort(a+1,a+n+1); build(1,n,0); F(i,1,m) { int x=read(),y=read(),k=read(); printf("%d\n",query(x,y,k,1,n,0)); } }