首页 > 代码库 > TreeSegment2104_DivideTree
TreeSegment2104_DivideTree
给出一数组a,然后要求查询a[i..j]的第k大的数
划分树
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 100005;
struct Node
{
int l,r;
int mid()
{
return (l+r)>>1;
}
} tree[N<<2];
int sorted[N];
int val[20][N],toLeft[20][N];
void build(int l,int r,int rt,int deep)// build(1,n,1,0);
{
tree[rt].l = l;//存的都是位置
tree[rt].r = r;
if(l == r) return;
int m = tree[rt].mid();
int midval = sorted[m];
int leftsame = m - l + 1;//表示在左子树上有多少和midval相等的数
for(int i = l ; i <= r ; i ++)
{
if(val[deep][i] < midval)
--leftsame;
}
int lpos = l,rpos = m + 1;
for(i = l ; i <= r ; i++)
{
if(i == l) toLeft[deep][i] = 0;//toleft[d][i]存的是d层在i之前(包括i)小于 sa[mid] 的数的数目
//另外toLeft[][i]指的是从tree[rt].l开始到tree[rt].r范围内的,而不是以为1为开始
else toLeft[deep][i] = toLeft[deep][i-1];//这里相当于对toLeft[][]初始化
if(val[deep][i] < midval)
{
++toLeft[deep][i];
val[deep+1][lpos++] = val[deep][i];
}
else if(val[deep][i] > midval)
{
val[deep+1][rpos++] = val[deep][i];
}
else//判断和midval相等的数是放在左部还是右部
{
if(leftsame > 0)
{
--leftsame;
++toLeft[deep][i];
val[deep+1][lpos++] = val[deep][i];
}
else//leftsame记录的左边与中间数值相等的个数。当左边的用完之后,其余的都是原先就在右边的
{
val[deep+1][rpos++] = val[deep][i];
}
}
}
build(l,m,rt<<1,deep+1);
build(m+1,r,rt<<1|1,deep+1);
}
int query(int l,int r,int k,int rt,int deep)//query(a,b,c,1,0)
{
if(l == r) return val[deep][l];
//下面就是要确认新的查找区间
int s;//表示[l,r]里在左边的数的个数//s表示区间[l,r]有多少个小于sa[mid]的数被分到左边
int ss;//表示[tree[rt].l...l-1]里在左边的数的个数
if(l == tree[rt].l)
{
s = toLeft[deep][r];
ss = 0;
}
else
{
ss = toLeft[deep][l-1];//ss并不是等于l-1,因为这l-1个数里面有的移到左边,有的移动到右边,这就是toLeft数组的作用
s = toLeft[deep][r] - ss;
}
if(s >= k)//区间【l,r】分到左边的数大于K,那么第K大的数也在左子树
{
/*进入左子树 */
int newl = tree[rt].l + ss;//在子树新的起点=子树起始点+前面更小的一段
int newr = newl + s - 1;//在子树新的终点
return query(newl,newr,k,rt<<1,deep+1);
}
else
{
int m = tree[rt].mid();
int b = r - l + 1 - s;//[L,R]这么一段本身的长度减去进入左子树的长度=进入右子树的长度(第K个在里面)
int bb = (l - 1) - tree[rt].l + 1 - ss;
//(l - 1) - tree[rt].l + 1表示[tree[rt].l,l-1]的长度,再减去它在左边部分的长度,得到[tree[rt].l,l-1]在右边的数的个数
int newl = m + 1 + bb;//m+1是中点的起点
int newr = m + b + bb;//m + r - l + 1 - toLeft[deep][r] + ss - l - tree[rt].l - ss = m+r-
return query(newl,newr,k-s,rt<<1|1,deep+1);
}
}
static inline int Rint()//这段是整型数的输入外挂,可以忽略不用看
{
struct X
{
int dig[256];
X()
{
for(int i = ‘0‘; i <= ‘9‘; ++i) dig[i] = 1;
dig[‘-‘] = 1;
}
};
static X fuck;
int s = 1, v = 0, c;
for (; !fuck.dig[c = getchar()];);
if (c == ‘-‘) s = 0;
else if (fuck.dig[c]) v = c ^ 48;
for (; fuck.dig[c = getchar()]; v = v * 10 + (c ^ 48));
return s ? v : -v;
}
int main()
{
int n,m;
while(~scanf("%d %d",&n,&m))
{
for(int i = 1 ; i <= n ; i++)
{
scanf("%d",&val[0][i]);
sorted[i] = val[0][i];
}
sort(sorted+1,sorted+n+1);
build(1,n,1,0);
while(m--)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
printf("%d\n",query(a,b,c,1,0));
}
}
return 0;
}
TreeSegment2104_DivideTree