蒟蒟蒟蒟蒟蒟蒟蒟蒟蒟蒟蒟蒟蒻 的博客,有错正常不过了,见谅见谅。
主席树模版,静态区间第K大
主席树用于解决类似这样的问题。 有一个数组a[],数组大小一般为1e5,有Q个询问,询问个数也是1e5。
每一个询问给出三个整数 l,r,k,问从区间 l 到 r 第 k 大的值是多少。
需要的前置知识:权值线段树。
权值线段树
用于解决类似这样的问题。 有一个数组a[],数组大小一般为1e5,含增、删、改操作,最后询问整个区间的第 K 大的值是多少。
权值线段树:简的来说,就是用下标代替值,然后维护区间和,即得到了区间包含的数字的个数。
例如:数组 a[] = {1,2,4,5}
就得到
sum[8] = 1,sum[9] = 1,sum[4] = 2,sum[2] = 2.
sum[6] = 1, sum[7] = 1, sum[3] = 2
sum[1] = 4.
那么对于区间[1 , 5] 的第 k 大就可以这样来查询;
如果 sum[右儿子] >= k , 递归向右儿子,表示第 k 大一定在右儿子的子树上,继续查询第 k 大。
else 递归向左儿子,表示右儿子占有前 sum[右儿子] 大,那么第 k 大一定在左儿子的子树上,查询第 k - sum[右儿子] 大
那么遇到数值范围很大怎么办呢? 离散化一下,就可以解决了。
权值线段树: bzoj 1503,可以用权值线段树去做一做。
以下所带数组的含义:K[] 表示原数组,X[] 表示复制K[] 然后用来离散化的数组,lson[i] , rson[i] 表示节点 i 的左儿子 与 右儿子。
C[] 代表一个结点的值,T[i] 表示第 i 颗线段树的根节点
静态主席树
简单可以理解为就是 n 颗权值线段树。第 i 棵权值线段树是根据区间 [1,i] 按值建立的。对于每一棵线段树我们记录它对应的区间每个数出现的次数,一般都需要对所有的数离散化。
那么对于区间 [l , r] 的信息,就可以用 第 r 颗权值线段树 - 第 l 颗权值线段树得到。
如果每一颗线段树都建立完整,那么肯定MLE。
对于第 i - 1 颗树 和 第 i 颗树,不同的点只有 log N 个,采用动态开点,每一次更新都新建 log N 个节点,然后和上一颗线段树连上,就完美解决了。
动态主席树
动态主席树相对于静态主席树 可以修改原数组的值,最后询问第k小。
新增数组含义:S[] 表示树状数组,S[i] 即 第 i 棵线段树的根节点,use[] 表示树状数组查询会经过的线段树的根结点
大致原理:对于修改,很显然 修改值 K[i] 影响的是 第 i 颗 前缀线段树 到 第 n 颗前缀线段树,所以使用树状数组来维护(每一个下标都代表一颗线段树,共 n 棵),起始时就是 n 棵构建好的空树,查询时就是 分别查询 树状数组 和 静态主席树 ,取和。
对于每一次修改,我们都先去除原影响,在添加新影响。
由于会带修改,所以离散化数组X[],需要包含修改后的数值,也就是得把修改后的值一并加入离散化数组(所以建立数组的时候要包含上询问的大小)。
那么动态主席树的大致模版步骤即:输入,数组K,并复制至数组X,然后输入询问,保存所有修改后的值至数组X,然后离散化,建立静态主席树,对树状数组初始化,最后处理所有询问。若是修改,则利用树状数组 先消除原影响,在添加新影响,是查询 就直接query即可。
这里是我学习时遇到的问题:
树状数组是怎么维护修改的:这里需要理解,树状数组起始时就 n 棵,而不是 m 棵。因为修改的时候影响是 第 i 到 n 颗前缀树,也就是每一棵树,维护一个对应位置的修改。树状数组查询时为什么就是简单的用C[lson[use[x]]], 其实这个和为什么query的都是用左儿子 一样的道理。要同步。
静态主席树第 k 小模版代码:
#include<bits/stdc++.h>
#define debug(x) cout << "[" << #x <<": " << (x) <<"]"<< endl
#define pii pair<int,int>
#define clr(a,b) memset((a),b,sizeof(a))
#define rep(i,a,b) for(int i = a;i < b;i ++)
#define pb push_back
#define MP make_pair
#define LL long long
#define ull unsigned LL
#define ls i << 1
#define rs (i << 1) + 1
#define fi first
#define se second
#define ptch putchar
#define CLR(a) while(!(a).empty()) a.pop()
using namespace std;
const int maxn = 2e5 + 10;
const int M = maxn * 40;
int T[maxn];///T[i]表示第i颗线段树的顶结点
int lson[M],rson[M],C[M];///c[i]就代表第i个点的权值,即上述权值线段树的sum
int X[maxn],K[maxn]; /// lson[i], rson[i] 表示第 i 个点的左儿子,右儿子是谁
int tot = 0,en; /// tot 用于动态开点
int getId(int x){
return lower_bound(X + 1,X + 1 + en,x) - X;
}
void build(int &i,int l,int r){
i = ++ tot;
C[i] = 0;
if(l == r) return ;
int mid = (l + r) >> 1;
build(lson[i],l,mid);
build(rson[i],mid + 1,r);
}
void update(int l,int r,int &i,int last,int pos){
C[++ tot] = C[last];
lson[tot] = lson[last];
rson[tot] = rson[last];
i = tot;
++ C[i];
if(l == r) return ;
int mid = (l + r) >> 1;
if(pos <= mid)
update(l,mid,lson[i],lson[last],pos);
else
update(mid + 1,r,rson[i],rson[last],pos);
}
int query(int l,int r,int lrt,int rrt,int k){
if(l == r) return l;
int mid = (l + r) >> 1;
int tmp = C[lson[rrt]] - C[lson[lrt]];
if(tmp >= k)
return query(l,mid,lson[lrt],lson[rrt],k);
return query(mid + 1,r,rson[lrt],rson[rrt],k - tmp);
}
int main() {
int n,m; scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++ i){
scanf("%d",&K[i]);
X[i] = K[i];
}
sort(X + 1,X + 1 + n);
en = unique(X + 1,X + 1 + n) - X - 1;
build(T[0],1,n);
for(int i = 1;i <= n;++ i)
update(1,n,T[i],T[i - 1],getId(K[i]));
while(m --){
int l,r,k; scanf("%d%d%d",&l,&r,&k);
printf("%d\n",X[query(1,n,T[l - 1],T[r],k)]);
}
return 0;
}
动态主席树第 k 小模版:
#include<bits/stdc++.h>
#define debug(x) cout << "[" << #x <<": " << (x) <<"]"<< endl
#define pii pair<int,int>
#define clr(a,b) memset((a),b,sizeof(a))
#define rep(i,a,b) for(int i = a;i < b;i ++)
#define pb push_back
#define MP make_pair
#define LL long long
#define ull unsigned LL
#define ls i << 1
#define rs i << 1 + 1
#define INT(t) int t; scanf("%d",&t)
using namespace std;
const int maxn = 6e4 + 10;
const int M = maxn * 40;
int T[maxn],S[maxn],use[maxn];
int lson[M],rson[M],C[M];
int K[maxn],X[maxn];
struct xx{
int l,r,k;
int x,val;
bool f;
}Q[maxn / 6];
int tot,cnt,en;
int n,m;
int build(int l,int r){
int rt = tot ++;
C[rt] = 0;
if(l != r){
int mid = (l + r) >> 1;
lson[rt] = build(l,mid);
rson[rt] = build(mid + 1,r);
}
return rt;
}
int update(int rt,int pos,int val){
int newrt = tot ++,tmp = newrt;
C[newrt] = C[rt] + val;
int l = 1,r = en;
while(l < r){
int mid = (l + r) >> 1;
if(pos <= mid){
r = mid;
lson[newrt] = tot ++;
rson[newrt] = rson[rt];
newrt = lson[newrt];
rt = lson[rt];
}
else {
l = mid + 1;
rson[newrt] = tot ++;
lson[newrt] = lson[rt];
newrt = rson[newrt];
rt = rson[rt];
}
C[newrt] = C[rt] + val;
}
return tmp;
}
#define lowbit(x) ((x) & (-x))
void add(int x,int pos,int val){
while(x <= n){
S[x] = update(S[x],pos,val);
x += lowbit(x);
}
}
int getSum(int x){
int res = 0;
while(x){
res += C[lson[use[x]]];
x -= lowbit(x);
}
return res;
}
int query(int lr,int rr,int k){
int lrt = T[lr - 1];
int rrt = T[rr];
for(int i = lr - 1;i ;i -= lowbit(i)) use[i] = S[i];
for(int i = rr;i ;i -= lowbit(i)) use[i] = S[i];
int l = 1,r = en;
while(l < r){
int tmp = getSum(rr) - getSum(lr - 1) + C[lson[rrt]] - C[lson[lrt]];
int mid = (l + r) >> 1;
if(tmp >= k){
r = mid;
for(int i = lr - 1;i ;i -= lowbit(i)) use[i] = lson[use[i]];
for(int i = rr;i ;i -= lowbit(i)) use[i] = lson[use[i]];
lrt = lson[lrt]; rrt = lson[rrt];
}
else {
l = mid + 1;
k -= tmp;
for(int i = lr - 1;i ;i -= lowbit(i)) use[i] = rson[use[i]];
for(int i = rr;i ;i -= lowbit(i)) use[i] = rson[use[i]];
lrt = rson[lrt]; rrt = rson[rrt];
}
}
return l;
}
int getId(int x){
return lower_bound(X + 1,X + 1 + en,x) - X;
}
int main() {
int t; scanf("%d",&t);
while(t --){
tot = cnt = 0;
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++ i){
scanf("%d",&K[i]);
X[++ cnt] = K[i];
}
char op;
for(int i = 1;i <= m;++ i){
scanf(" %c",&op);
if(op == 'Q'){
int l,r,k; scanf("%d%d%d",&l,&r,&k);
Q[i].l = l; Q[i].r = r; Q[i].k = k;
Q[i].f = 0;
}
else {
int x,val; scanf("%d%d",&x,&val);
Q[i].x = x; Q[i].val = val;
X[++ cnt] = val;
Q[i].f = 1;
}
}
sort(X + 1,X + 1 + cnt);
en = unique(X + 1,X + 1 + cnt) - X - 1;
T[0] = build(1,en);
for(int i = 1;i <= n;++ i){
T[i] = update(T[i - 1],getId(K[i]),1);
}
for(int i = 1;i <= n;++ i) S[i] = T[0];
for(int i = 1;i <= m;++ i){
if(Q[i].f == 1){
add(Q[i].x,getId(K[Q[i].x]),-1);
add(Q[i].x,getId(Q[i].val),1);
K[Q[i].x] = Q[i].val;
}
else {
// cout << " 1" << endl;
printf("%d\n",X[query(Q[i].l,Q[i].r,Q[i].k)]);
}
}
}
return 0;
}