题意:给定一个序列key[1..n]和m个询问{s,t,rank}(1 <= n <= 100 000, 1 <= m <= 5 000),对于每个询问输出区间[s,t]中第rank小的值
分析:由于2761和这题差不多,且数据量是这题的10倍,所以我一开始就把2761的SBT代码交上去,结果竟然是TLE,估计是栽在了"Case Time Limit: 2000MS"上面了。最终还是用了别人的思路,由此接触到一种很巧妙的结构:归并树
归并树可以用简单的一句话概括:利用类似线段树的树型结构记录合并排序的过程。
回顾一下如何利用归并树解决这道题:
1,建立归并树后我们得到了序列key[]的非降序排列,由于此时key[]内元素的rank是非递减的,因此key[]中属于指定区间[s,t]内的元素的rank也是非递减的,所以我们可以用二分法枚举key[]中的元素并求得它在[s,t]中的rank值,直到该rank值和询问中的rank值相等;
2,那对于key[]中的某个元素val,如何求得它在指定区间[s,t]中的rank?这就要利用到刚建好的归并树:我们可以利用类似线段树的query[s,t]操作找到所有属于[s,t]的子区间,然后累加val分别在这些子区间内的rank,得到的就是val在区间[s,t]中的rank,注意到这和合并排序的合并过程一致;
3,由于属于子区间的元素的排序结果已经记录下来,所以val在子区间内的rank可以通过二分法得到。
上面三步经过了三次二分操作(query也是种二分),于是每次询问的复杂度是O(log n * log n * log n)
PS:写二分查找时要注意细节。。
/*
Problem: 2104 User: zgmf_x20a
Memory: 9708K Time: 2454MS
Language: C++ Result: Accepted
*/
#include
<
iostream
>
#include
<
algorithm
>
using
namespace
std;
#define
MAXN 100000+5
#define
LOGMAXN 17+5
int
sortseq[LOGMAXN][MAXN],n,q,key[MAXN],s,t,rank;
struct
Node{
int
l,r;
}nod[
3
*
MAXN];
void
buildtree(
int
u,
int
l,
int
r,
int
deep){
int
i,j,m,loop;
nod[u].l
=
l;
nod[u].r
=
r;
if
(l
==
r){
sortseq[deep][l]
=
key[l];
return
;
}
m
=
(l
+
r)
/
2
;
buildtree(
2
*
u,l,m,deep
+
1
);
buildtree(
2
*
u
+
1
,m
+
1
,r,deep
+
1
);
i
=
l;j
=
m
+
1
;loop
=
l;
while
(i
<=
m
&&
j
<=
r){
if
(sortseq[deep
+
1
][i]
<
sortseq[deep
+
1
][j])
sortseq[deep][loop
++
]
=
sortseq[deep
+
1
][i
++
];
else
sortseq[deep][loop
++
]
=
sortseq[deep
+
1
][j
++
];
}
if
(i
==
m
+
1
)
while
(j
<=
r)
sortseq[deep][loop
++
]
=
sortseq[deep
+
1
][j
++
];
else
while
(i
<=
m)
sortseq[deep][loop
++
]
=
sortseq[deep
+
1
][i
++
];
}
int
query(
int
u,
int
val,
int
deep){
if
(s
<=
nod[u].l
&&
nod[u].r
<=
t)
return
lower_bound(
&
sortseq[deep][nod[u].l],
&
sortseq[deep][nod[u].r]
+
1
,val)
-&
sortseq[deep][nod[u].l];
//
*
int
res
=
0
;
if
(s
<=
nod[
2
*
u].r)
res
+=
query(
2
*
u,val,deep
+
1
);
if
(t
>=
nod[
2
*
u
+
1
].l)
res
+=
query(
2
*
u
+
1
,val,deep
+
1
);
return
res;
}
int
main(){
int
i,l,r,m,pos;
while
(scanf(
"
%d%d
"
,
&
n,
&
q)
!=
EOF){
for
(i
=
1
;i
<=
n;i
++
)
scanf(
"
%d
"
,
&
key[i]);
buildtree(
1
,
1
,n,
1
);
while
(q
--
){
scanf(
"
%d%d%d
"
,
&
s,
&
t,
&
rank);
rank
--
;
//
*
l
=
1
;r
=
n;
while
(l
<
r){
m
=
(l
+
r
+
1
)
/
2
;
//
*
pos
=
query(
1
,sortseq[
1
][m],
1
);
if
(pos
<=
rank)
l
=
m;
else
r
=
m
-
1
;
}
printf(
"
%d\n
"
,sortseq[
1
][l]);
}
}
return
0
;
}
另附lower/upper_bound的用法:
lower_bound(k) |
p=c.lower_bound(k)
|
*p>=k 且 p 最前 |
upper_bound(k) |
p=c.upper_bound(k)
|
*p>k 且 p 最前 |