这道题应该是很不错的板子了\(\mathcal{\color{cyan}{Link}}\)

\(\mathcal{\color{red}{Description}}\)

给定一个序列,有两种操作。一是要查询区间第\(k\)大,二是要支持单点修改某个元素的值。

\(\mathcal{\color{red}{Solution}}\)

我们考虑……对于支持修改这种操作……好像对于前缀和支持的函数式线段树很不友好……那我们考虑有没有什么别的可以维护前缀的东西来处理插入操作——嗯,树状数组就闪耀登场了!这玩意儿虽然对于查询好像是\(logn\)的,比朴素的前缀查询多了一个\(log\)……也就是\(O(nlog^2n)\)的时间复杂度。\(emmmm\)好像有种方式可以少一个\(log\)。以后等我什么时候会了再补上吧!

而这里我们需要注意的是,我们需要事先把所有的修改后的值离线下来,也就是说我们要把所有的修改操作离线到我们需要离散化的建树数组里。原因嘛……是因为我们的主席树是权值线段树,每个节点上的值是这个权值的出现次数。所以如果你一开始不给它留个空位置的话,它是无法被插入的。

    for(i = 1; i <= N; i ++)
base[i] = aft[++ tot]= qr() ;
for(i = 1; i <= M; i ++){
cin >> mark ;
if(mark == 'Q') q[i].a = qr(), q[i].b = qr(), q[i].c = qr() ;
else q[i].x = qr(), q[i].y = aft[++ tot] =qr() ;
}
sort(aft + 1, aft + tot + 1 ) ;
Len = unique(aft + 1, aft + tot + 1) - aft - 1 ;

嗯,也就是说一个在线的算法又被离线了虽然对此并没有什么办法只能统统二十块

而换用树状数组之后,我们的函数式线段树的递推式子就不再是\(T_i = T_{i - 1} + f(T_i)\)了,而是$$T_i = \sum\limits_{bit(i_j) = 1}^{}{T_j}$$其中\(j\)表示\(i\)的二进制位的第\(j\)位。

而由于我们是顺序递推的,所以我们在\(insert\)时,静态中的\(last_i\)要是\(T_i\)而不是\(T_{i - 1}\),也就是说每次我们要把所有包括\(j\)的、之后待修改的状态更新\(+1\)

,大概就是这种感觉:

il int insert(int &rt, int last, int l, int r, int pos, int v){
rt = ++ cnt ;
sum[rt] = sum[last] + v ;
L[rt] = L[last] ;
R[rt] = R[last] ;
if (l < r){
if(pos <= mid) L[rt] = insert(L[rt], L[last], l, mid, pos, v) ;
else R[rt] = insert(R[rt], R[last], mid + 1, r, pos, v) ;
}
return rt ;
}
il void add(int x, int v){
pos = lower_bound(aft + 1, aft + Len + 1, base[x]) - aft ;
for(j = x; j <= N; j += lowbit(j)){
T[j] = insert(T[j], T[j], 1, Len, pos, v) ;
}
}

那个那个\(insert\)……就是就是静态主席树中的\(insert\)。

之后就到了查询的操作,查询的时候……这个这个由于牵扯到多个树的求和,所以我们每次递归要进行两次的\(logn\)的操作。

    for(i = 1; i <= M; i ++){
if (q[i].c){
totl = totr = 0 ;
for(j = q[i].a - 1; j ; j -= lowbit(j))
LL[++ totl] = T[j] ;
for(j = q[i].b; j ; j -= lowbit(j))
RR[++ totr] = T[j] ;
printf("%d\n", aft[query(1, Len, q[i].c)]) ;
}
else{
add(q[i].x, -1) ;
base[q[i].x] = q[i].y ;
add(q[i].x, 1) ;
}
}
- - - - - - - - - - - - - - - - - - - -
int query(int l, int r, int q){
if(l == r) return l ;
int chk = 0 ;
for(int k = 1; k <= totl; k ++) chk -= sum[L[LL[k]]] ;
for(int k = 1; k <= totr; k ++) chk += sum[L[RR[k]]] ;
if(q <= chk){
for(int k = 1; k <= totl; k ++) LL[k] = L[LL[k]] ;
for(int k = 1; k <= totr; k ++) RR[k] = L[RR[k]] ;
return query(l, mid, q) ;
}
else {
for(int k = 1; k <= totl; k ++) LL[k] = R[LL[k]] ;
for(int k = 1; k <= totr; k ++) RR[k] = R[RR[k]] ;
return query(mid + 1, r, q - chk) ;
}
}

嗯,我们每次都需要去求前几个树的和,所以……就没了。

#include <cstdio>
#include <iostream>
#include <algorithm>
#define il inline
#define MAXN 200001
#define mid ((l + r) >> 1) using namespace std ;
char mark ;
int tot, pos, cnt, aft[MAXN];
int Len, L[MAXN << 6], R[MAXN << 6], T[MAXN << 6], sum[MAXN << 6];
int LL[MAXN], RR[MAXN] ;
int N, M, base[MAXN], i, j, totl, totr;
struct node{
int a, b, c, x, y ;
}q[MAXN]; il int lowbit(int x){
return x & (-x) ;
}
il int qr(){
int k = 0, f = 1;
char c = getchar() ;
while(!isdigit(c)){
if (c == '-') f = -1 ;
c = getchar() ;
}
while(isdigit(c)){
k = (k << 1) + (k << 3) + c - 48 ;
c = getchar() ;
}
return k * f;
}
il int insert(int &rt, int last, int l, int r, int pos, int v){
rt = ++ cnt ;
sum[rt] = sum[last] + v ;
L[rt] = L[last] ;
R[rt] = R[last] ;
if (l < r){
if(pos <= mid) L[rt] = insert(L[rt], L[last], l, mid, pos, v) ;
else R[rt] = insert(R[rt], R[last], mid + 1, r, pos, v) ;
}
return rt ;
}
il void add(int x, int v){
pos = lower_bound(aft + 1, aft + Len + 1, base[x]) - aft ;
for(j = x; j <= N; j += lowbit(j)){
T[j] = insert(T[j], T[j], 1, Len, pos, v) ;
}
}
int query(int l, int r, int q){
if(l == r) return l ;
int chk = 0 ;
for(int k = 1; k <= totl; k ++) chk -= sum[L[LL[k]]] ;
for(int k = 1; k <= totr; k ++) chk += sum[L[RR[k]]] ;
if(q <= chk){
for(int k = 1; k <= totl; k ++) LL[k] = L[LL[k]] ;
for(int k = 1; k <= totr; k ++) RR[k] = L[RR[k]] ;
return query(l, mid, q) ;
}
else {
for(int k = 1; k <= totl; k ++) LL[k] = R[LL[k]] ;
for(int k = 1; k <= totr; k ++) RR[k] = R[RR[k]] ;
return query(mid + 1, r, q - chk) ;
}
}
int main(){
cin >> N >> M ;
for(i = 1; i <= N; i ++)
base[i] = aft[++ tot]= qr() ;
for(i = 1; i <= M; i ++){
cin >> mark ;
if(mark == 'Q') q[i].a = qr(), q[i].b = qr(), q[i].c = qr() ;
else q[i].x = qr(), q[i].y = aft[++ tot] =qr() ;
}
sort(aft + 1, aft + tot + 1 ) ;
Len = unique(aft + 1, aft + tot + 1) - aft - 1 ;
for(i = 1; i <= N; i ++) add(i, 1) ;
for(i = 1; i <= M; i ++){
if (q[i].c){
totl = totr = 0 ;
for(j = q[i].a - 1; j ; j -= lowbit(j))
LL[++ totl] = T[j] ;
for(j = q[i].b; j ; j -= lowbit(j))
RR[++ totr] = T[j] ;
printf("%d\n", aft[query(1, Len, q[i].c)]) ;
}
else{
add(q[i].x, -1) ;
base[q[i].x] = q[i].y ;
add(q[i].x, 1) ;
}
}
}

最新文章

  1. Web报表工具FineReport填报界面键盘操作
  2. 解决Window Azure: Failed to start Development Storage: the SQL Server instance ‘localhost\SQLExpress’ could not be found.
  3. final评论II
  4. 在jfinal中使用druid,并配置查看权限
  5. python合并2个字典
  6. mysql 累加排序求名次
  7. timeout connect 10000 # default 10 second time out if a backend is not found
  8. MVAPI第一个版本架构图
  9. ActiveMQ的消息持久化机制
  10. Mockito框架入门教程(二)
  11. scp 实现文件打包上传到linux
  12. Sqlite之事务
  13. [20180316]理解db file parallel read等待事件.txt
  14. .Net-using-Class:MemoryCache 类
  15. win8 中如何删除 共享文件夹 用户名和密码
  16. 【进阶修炼】&mdash;&mdash;改善C#程序质量(9)
  17. idea appliction context not configured for this file
  18. 2.批处理内部命令之REM 和::
  19. [面试题] Find next higher number with same digits
  20. Openssl rand命令

热门文章

  1. 搭建 Visual Studio 2012 + DXperience-13.2.6 + MySql 开发平台
  2. Struts2 (四) — 拦截器
  3. drupal7 profile2模块获取个人信息
  4. 全面认识Docker和基本指令
  5. jQuery的attr()与prop()的区别
  6. 微信小程序-movable-view
  7. 在主线程中慎用WaitForSingleObject (WaitForMultipleObjects)
  8. AutoCAD LoadLibrary Failed with error 126 Message
  9. jquery 仿windows10菜单效果下载
  10. Redis的基本操作语句