The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the k-th smallest number of the given N numbers. They have developed a more powerful system such that for N numbers a[1], a[2], ..., a[N], you can ask it like: what is the k-th smallest number of a[i], a[i+1], ..., a[j]? (For some i<=j, 0<k<=j+1-i that you have given to it). More powerful, you can even change the value of some a[i], and continue to query, all the same.

Your task is to write a program for this computer, which

- Reads N numbers from the input (1 <= N <= 50,000)

- Processes M instructions of the input (1 <= M <= 10,000). These instructions include querying the k-th smallest number of a[i], a[i+1], ..., a[j] and change some a[i] to t.

Input

The first line of the input is a single number X (0 < X <= 4), the number of the test cases of the input. Then X blocks each represent a single test case.

The first line of each block contains two integers N and M, representing N numbers and M instruction. It is followed by N lines. The (i+1)-th line represents the number a[i]. Then M lines that is in the following format

Q i j k or
C i t

It represents to query the k-th number of a[i], a[i+1], ..., a[j] and change some a[i] to t, respectively. It is guaranteed that at any time of the operation. Any number a[i] is a non-negative integer that is less than 1,000,000,000.

There're NO breakline between two continuous test cases.

Output

For each querying operation, output one integer to represent the result. (i.e. the k-th smallest number of a[i], a[i+1],..., a[j])

There're NO breakline between two continuous test cases.

Sample Input

2
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

Sample Output

3
6
3
6

参考了大佬的博客:https://www.cnblogs.com/Empress/p/4659824.html

题意就是求区间内的第 k 小的数,但是会改变区间里的数。

因为数很大,所以首先要把所有的 a[i] ,包括更新过程的 a[i],然后拿去离散化。

对于求区间第 k 大,基本思想还是和静态区间求第 k 小一样,先求前缀和,然后相减。然后得到区间内的数量关系,就可以求区间内的第 k 小了。但是这里有更新操作,原本的求区间的 node[node[y].l].sum - node[node[x].l].sum 就不能直接用了。

这里我们维护两颗完全不同的树,对一开始的数列用普通的主席树来维护,对于更新操作,用一个树状数组来维护,树状数组的每个节点都是一颗树,因为是单点更新,每棵树都只要更新 log(n) 个节点,用主席树来更新。(一个只维护初始的样子,一个只维护更新的样子)

然后这样的话,我们只需要想办法求出在 R 树和 L 树在某个区间上的左边部分的差值,在加上 node[node[y].l].sum - node[node[x].l].sum,就可以确定第 k 小在这个区间的左边还是右边。然后就可以开始用主席树的套路了。

然后这里为了求 R 树和 L 树在某个区间内的差值,我们定义一个 use 表示树状数组的目前结点对应的树是哪一颗,use 的更新类似于主席树递归时候从x -> node[x].l 这样子的过程,都是为了把它往左整体往左更新。.......其实还是感觉很抽象的东西

 /*
.
';;;;;.
'!;;;;;;!;`
'!;|&#@|;;;;!:
`;;!&####@|;;;;!:
.;;;!&@$$%|!;;;;;;!'.`:::::'.
'!;;;;;;;;!$@###&|;;|%!;!$|;;;;|&&;.
:!;;;;!$@&%|;;;;;;;;;|!::!!:::;!$%;!$%` '!%&#########@$!:.
;!;;!!;;;;;|$$&@##$;;;::'''''::;;;;|&|%@$|;;;;;;;;;;;;;;;;!$;
;|;;;;;;;;;;;;;;;;;;!%@#####&!:::;!;;;;;;;;;;!&####@%!;;;;$%`
`!!;;;;;;;;;;!|%%|!!;::;;|@##%|$|;;;;;;;;;;;;!|%$#####%;;;%&;
:@###&!:;;!!||%%%%%|!;;;;;||;;;;||!$&&@@%;;;;;;;|$$##$;;;%@|
;|::;;;;;;;;;;;;|&&$|;;!$@&$!;;;;!;;;;;;;;;;;;;;;;!%|;;;%@%.
`!!;;;;;;;!!!!;;;;;$@@@&&&&&@$!;!%|;;;;!||!;;;;;!|%%%!;;%@|.
%&&$!;;;;;!;;;;;;;;;;;|$&&&&&&&&&@@%!%%;!||!;;;;;;;;;;;;;$##!
!%;;;;;;!%!:;;;;;;;;;;!$&&&&&&&&&&@##&%|||;;;!!||!;;;;;;;$&:
':|@###%;:;;;;;;;;;;;;!%$&&&&&&@@$!;;;;;;;!!!;;;;;%&!;;|&%.
!@|;;;;;;;;;;;;;;;;;;|%|$&&$%&&|;;;;;;;;;;;;!;;;;;!&@@&'
.:%#&!;;;;;;;;;;;;;;!%|$$%%&@%;;;;;;;;;;;;;;;;;;;!&@:
.%$;;;;;;;;;;;;;;;;;;|$$$$@&|;;;;;;;;;;;;;;;;;;;;%@%.
!&!;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;|@#;
`%$!;;;;;;;;;;;$@|;;;;;;;;;;;;;;;;;;;;;;;;!%$@#@|.
.|@%!;;;;;;;;;!$&%||;;;;;;;;;;;;;;;;;!%$$$$$@#|.
;&$!;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%#####|.
|##$|!;;;;;;::'':;;;;;;;;;;;;;!%$$$@#@;
;@&|;;;;;;;::'''''':;;;;;;;|$&@###@|`
.%##@|;;;;:::''''''''''::;!%&##$'
`$##@$$@@&|!!;;;:'''''::::;;;;;|&#%.
;&@##&$%!;;;;;;::''''''''::;!|%$@#@&@@:
.%@&$$|;;;;;;;;;;:'''':''''::;;;%@#@@#%.
:@##@###@$$$$$|;;:'''':;;!!;;;;;;!$#@@#$;`
`%@$$|;;;;;;;;:'''''''::;;;;|%$$|!!&###&'
|##&%!;;;;;::''''''''''''::;;;;;;;!$@&:`!'
:;!@$|;;;;;;;::''''''''''':;;;;;;;;!%&@$: !@#$'
|##@@&%;;;;;::''''''''':;;;;;;;!%&@#@$%: '%%!%&;
|&%!;;;;;;;%$!:''''''':|%!;;;;;;;;|&@%||` '%$|!%&;
|@%!;;!!;;;||;:'''''':;%$!;;;;!%%%&#&%$&: .|%;:!&%`
!@&%;;;;;;;||;;;:''::;;%$!;;;;;;;|&@%;!$; `%&%!!$&:
'$$|;!!!!;;||;;;;;;;;;;%%;;;;;;;|@@|!$##; !$!;:!$&:
|#&|;;;;;;!||;;;;;;;;!%|;;;;!$##$;;;;|%' `%$|%%;|&$'
|&%!;;;;;;|%;;;;;;;;$$;;;;;;|&&|!|%&&; .:%&$!;;;:!$@!
`%#&%!!;;;;||;;;;;!$&|;;;!%%%@&!;;;!!;;;|%!;;%@$!%@!
!&!;;;;;;;;;||;;%&!;;;;;;;;;%@&!;;!&$;;;|&%;;;%@%`
'%|;;;;;;;;!!|$|%&%;;;;;;;;;;|&#&|!!||!!|%$@@|'
.!%%&%'`|$; :|$#%|@#&;%#%.
*/
#include <map>
#include <set>
#include <list>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <cstdio>
#include <bitset>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lowbit(x) x & (-x)
#define mes(a, b) memset(a, b, sizeof a)
#define fi first
#define se second
#define pii pair<int, int>
#define INOPEN freopen("in.txt", "r", stdin)
#define OUTOPEN freopen("out.txt", "w", stdout) typedef unsigned long long int ull;
typedef long long int ll;
const int maxn = 5e4 + ;
const int maxm = 1e5 + ;
const ll mod = 1e9 + ;
const ll INF = 1e18 + ;
const int inf = 0x3f3f3f3f;
const double pi = acos(-1.0);
const double eps = 1e-;
using namespace std; int n, m;
int cas, tol, T;
struct Node{
int l, r;
int sum;
} node[maxn * ];
struct Ask{
int l, r, k;
int id;
} ask[maxn / ];
int a[maxn];
int S[maxn]; //树状数组的根
int rt[maxn]; //主席树的根
int use[maxn]; //use表示树状数组的各个结点对应的树是哪一颗
vector<int> vv;
int L, R; int getid(int x) {
return lower_bound(vv.begin(), vv.end(), x) - vv.begin() + ;
} void init() {
tol = ;
mes(a, );
mes(S, );
mes(rt, );
vv.clear();
mes(use, );
mes(ask, );
mes(node, );
} int Sum(int pos) {
// 各颗树从 S[i] 走到了 used[i] 的位置,然后求左边部分的和
int ans = ;
for(int i=pos; i; i-=lowbit(i))
ans += node[node[use[i]].l].sum;
return ans;
} void update(int l, int r, int &x, int y, int pos, int val) {
x = ++tol;
node[tol] = node[y];
node[tol].sum += val;
if(l == r) return ;
int mid = (l + r) >> ;
if(pos <= mid)
update(l, mid, node[x].l, node[y].l, pos, val);
else
update(mid+, r, node[x].r, node[y].r, pos, val);
} void add(int pos, int k, int v) {
for(int i=pos; i<=n; i+=lowbit(i)) {
update(, vv.size(), S[i], S[i], k, v);
}
} int query(int l, int r, int x, int y, int k) {
if(l == r) return l;
int mid = (l + r) >> ;
//注意树状数组是 L 和 R
//因为是找 L 和 R 树链上的到达 use 位置的值
int tmp = Sum(R) - Sum(L) + node[node[y].l].sum - node[node[x].l].sum;
if(k <= tmp) {
//往现在到达的x,y位置的两棵树的左边查询
//把 L 和 R 这路径上的所有树状数组都往左子树更新
for(int i=L; i; i-=lowbit(i)) use[i] = node[use[i]].l;
for(int i=R; i; i-=lowbit(i)) use[i] = node[use[i]].l;
return query(l, mid, node[x].l, node[y].l, k);
} else {
//往现在到达的x,y位置的两棵树的右边查询
//把 L 和 R 这路径上的所有树状数组都往右子树更新
for(int i=L; i; i-=lowbit(i)) use[i] = node[use[i]].r;
for(int i=R; i; i-=lowbit(i)) use[i] = node[use[i]].r;
return query(mid+, r, node[x].r, node[y].r, k-tmp);
}
} int main() {
scanf("%d", &T);
while(T--) {
init();
scanf("%d%d", &n, &m);
for(int i=; i<=n; i++) {
scanf("%d", &a[i]);
vv.push_back(a[i]);
}
for(int i=; i<=m; i++) {
char ss[];
scanf("%s", ss);
if(ss[] == 'Q') {
ask[i].id = ;
scanf("%d%d%d", &ask[i].l, &ask[i].r, &ask[i].k);
} else {
ask[i].id = ;
scanf("%d%d", &ask[i].l, &ask[i].r);
vv.push_back(ask[i].r);
}
}
sort(vv.begin(), vv.end()); //离散化一下
vv.erase(unique(vv.begin(), vv.end()), vv.end());
for(int i=; i<=n; i++) {
int id = getid(a[i]); //第一颗树
update(, vv.size(), rt[i], rt[i-], id, );
//vv里面的数可能不止有 n 个,切记不可以用 n
}
for(int i=; i<=m; i++) {
if(ask[i].id) {
L = ask[i].l-, R = ask[i].r;
//从 L 和 R 位置开始, use 表示的从 L, R 的S根开始
for(int j=L; j; j-=lowbit(j)) use[j] = S[j];
for(int j=R; j; j-=lowbit(j)) use[j] = S[j];
int pos = query(, vv.size(), rt[L], rt[R], ask[i].k);
printf("%d\n", vv[pos-]);
} else { //第二颗树
int id = getid(a[ask[i].l]);
add(ask[i].l, id, -); //先把之前的清除掉,在加现在的
id = getid(ask[i].r);
add(ask[i].l, id, );
a[ask[i].l] = ask[i].r;
}
}
}
return ;
}

Dynamic Rankings ZOJ - 2112(主席树+树状数组)的更多相关文章

  1. 动态主席树【带修改】&& 例题 Dynamic Rankings ZOJ - 2112

    参考链接:https://blog.csdn.net/WilliamSun0122/article/details/77885781 一.动态主席树介绍 动态主席树与静态主席树的不同在于:静态主席树不 ...

  2. 洛谷 P2617 Dynamic Rankings || ZOJ - 2112

    写的让人看不懂,仅留作笔记 静态主席树,相当于前缀和套(可持久化方法构建的)值域线段树. 建树方法:记录前缀和的各位置的线段树的root.先建一个"第0棵线段树",是完整的(不需要 ...

  3. ZOJ - 2112 主席树套树状数组

    题意:动态第k大,可单点更新,操作+原数组范围6e4 年轻人的第一道纯手工树套树 静态第k大可以很轻易的用权值主席树作差而得 而动态第k大由于修改第i个数会影响[i...n]棵树,因此我们不能在原主席 ...

  4. BZOJ1901 Dynamic Rankings|带修主席树

    题目链接:戳我 其实我并不会做,于是看了题解 我们都知道主席树是利用前缀和记录历史版本来搞区间K大的一种数据结构.不过一般的主席树只能搞定静态区间第K大.如果带修怎么办呢? 想一下...单点修改+区间 ...

  5. BZOJ 1901: Zju2112 Dynamic Rankings | 带修改主席树

    题目: emmmm是个权限题 题解: 带修改主席树的板子题,核心思想是用树状数组维护动态前缀和的性质来支持修改 修改的时候修改类似树状数组一样进行logn个Insert 查询的时候同理,树状数组的方法 ...

  6. [luogu P2617] Dynamic Rankings 带修主席树

    带修改的主席树,其实这种,已经不能算作主席树了,因为这个没有维护可持久化的... 主席树直接带修改的话,由于这种数据结构是可持久化的,那么要相应改动,这个节点以后所有的主席树,这样单次修改,就达到n* ...

  7. 【BZOJ-1901】Dynamic Rankings 带修主席树

    1901: Zju2112 Dynamic Rankings Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 7292  Solved: 3038[Su ...

  8. bzoj1901: Zju2112 Dynamic Rankings(BIT套主席树)

    带修改的题主席树不记录前缀,只记录单点,用BIT统计前缀.  对于BIT上每一个点建一棵主席树,修改和询问的时候用BIT跑,在主席树上做就行了.  3k4人AC的题#256...应该不算慢 #incl ...

  9. CodeForces -163E :e-Government (AC自动机+DFS序+树状数组)

    The best programmers of Embezzland compete to develop a part of the project called "e-Governmen ...

随机推荐

  1. 智能POS(轻餐、正餐同理)桌台页面已结金额,只做参考,不做对账使用

    智能POS桌台已结金额只用来做参考使用,不做对账保障: 已结金额只有桌台一次结账金额,若存在反结账或退款的情况则不会减去相应的已结金额: 点餐无桌台的订单,金额不做统计: 口碑订单.扫码点餐,金额不做 ...

  2. python 通过元类控制类的创建

    一.python中如何创建类? 1. 直接定义类 class A: a = 'a' 2. 通过type对象创建 在python中一切都是对象 在上面这张图中,A是我们平常在python中写的类,它可以 ...

  3. 010 Editor v8.0.1(32 - bit) 算法逆向分析、注册机编写

    010 Editor 的逆向分析整体算下来还是比较简单的,将程序拖入OD,通过字符串搜索定位到核心代码,经过分析,主要是如下图所示的两个关键函数,返回正确的值,才算是注册成功. 00409C9B 这个 ...

  4. Python基础之协程

    阅读目录 一 引子 二 协程介绍 三 Greenlet模块 四 Gevent模块 引子 之前我们学习了线程.进程的概念,了解了在操作系统中 进程是资源分配的最小单位,线程是CPU调度的最小单位. 按道 ...

  5. Spring-扫描注解原理,注解自动扫描原理分析

    注解自动扫描原理分析 在spring的配置文件中加入如下代码,spring便开启了自动扫描,那么它的底层到底是如何实现的呢? <context:component-scan base-packa ...

  6. 文本分类实战(七)—— Adversarial LSTM模型

    1 大纲概述 文本分类这个系列将会有十篇左右,包括基于word2vec预训练的文本分类,与及基于最新的预训练模型(ELMo,BERT等)的文本分类.总共有以下系列: word2vec预训练词向量 te ...

  7. PHP依赖注入原理与用法分析

    https://www.jb51.net/article/146025.htm 本文实例讲述了PHP依赖注入原理与用法.分享给大家供大家参考,具体如下: 引言 依然是来自到喜啦的一道面试题,你知道什么 ...

  8. MySql数据库在NodeJS中简单的基本操作

    阅读目录 一:连接数据库 二:数据的增删改查操作 2.1 数据库新增和查询数据 2.2 获取该数据的主键值 2.3 多语句查询 回到顶部 一:连接数据库 const mysql = require(' ...

  9. springBoot中使用定时任务

    简单示例 导入依赖 springBoot已经默认集成了定时任务的依赖,只需要引入基本的依赖就可以使用定时任务. <parent> <groupId>org.springfram ...

  10. Linux内存管理 (7)VMA操作

    专题:Linux内存管理专题 关键词:VMA.vm_area_struct.查找/插入/合并VMA.红黑树. 用户进程可以拥有3GB大小的空间,远大于物理内存,那么这些用户进程的虚拟地址空间是如何管理 ...