算法问题描述
区间维护两维的信息,其中一维是可减的。
更宽泛地说,题目需要建立多棵线段树,但不同线段树中有很多信息相同,这种情况下就可以使用可持续化线段树。
例题
题目描述
给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值。
输入格式:
第一行包含两个正整数N、M,分别表示序列的长度和查询的个数。
第二行包含N个正整数,表示这个序列各项的数字。
接下来M行每行包含三个整数 l, r, k 表示查询区间 [l, r] 内的第k小值。
输出格式
输出包含k行,每行1个正整数,依次表示每一次查询的结果
思路
如果是查询整个序列第k小的数,显然我们可以直接排序,但考虑用值域线段树去做。
先离散化值域,二分答案,在线段树上查找它前面有几个数
如果是查询区间[f,r]我们同样二分答案,查询在1-f中小于这个数的数的个数有几个,和在1-r中小于这个数的数的个数有几个,相减得到这个答案在[f,r]中排第几个
主席树部分!!!
难点在我们要在[1-i]的区间分别建n棵线段树,空间爆炸,如果有修改操作则时间也爆炸。
看着一棵维护[1,n]的结构完整线段树,我们每此从[1,i]的线段树到[1,i+1]的线段树的时候只有一条链被添加(更改),即我们会夹在上棵树的中间建一条链(值域线段树),其他信息是一样的,于是我们在建左右孩子的时候用动态加点+指针指向上个图中的点的方式建树即可。每次连边的时候判断我是夹在当前左边的那个还是右边的那个。
代码
//代码中的fs和rs是可以指针一样的东西,指向左右孩子在线段树里的下标!
#include <bits/stdc++.h>
#define maxn 1000005
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
int n,m,cnt;
int rt[maxn],rnk[maxn],tid[maxn];
struct NODE{
int x,y,hash;
}arr[maxn];
bool cmpx(NODE i,NODE j){return i.x<j.x;}
bool cmpy(NODE i,NODE j){return i.y<j.y;}
struct SG{
int w,fs,rs;
}tr[maxn<<2];
void build(int o,int f,int r){
if(f!=r){
int mid=f+r>>1;
tr[o].fs=++cnt;build(cnt,f,mid);
tr[o].rs=++cnt;build(cnt,mid+1,r);
}
}
void insert(int o,int _o,int f,int r,int k){
if(f==r){
tr[o].w++;
return;
}
int mid=f+r>>1;
if(mid<k){
tr[o].fs=tr[_o].fs;
tr[o].rs=++cnt;
insert(cnt,tr[_o].rs,mid+1,r,k);
}
else{
tr[o].rs=tr[_o].rs;
tr[o].fs=++cnt;
insert(cnt,tr[_o].fs,f,mid,k);
}
tr[o].w=tr[tr[o].fs].w+tr[tr[o].rs].w;
}
int query(int ff,int rr,int k,int f,int r){
if(f==r) return rnk[f];
int mid=f+r>>1,fw=tr[tr[ff].fs].w,rw=tr[tr[rr].fs].w;
if(rw-fw<k) return query(tr[ff].rs,tr[rr].rs,k-rw+fw,mid+1,r);
else return query(tr[ff].fs,tr[rr].fs,k,f,mid);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&arr[i].x);
arr[i].y=i;
}
sort(arr+1,arr+n+1,cmpx);
for(int i=1;i<=n;i++) arr[i].hash=i,rnk[i]=arr[i].x;
sort(arr+1,arr+n+1,cmpy);
build(++cnt,1,n);
rt[0]=1;
for(int i=1;i<=n;i++){
rt[i]=++cnt;
insert(rt[i],rt[i-1],1,n,arr[i].hash);
}
for(int i=1;i<=m;i++){
int ff,rr,k;
scanf("%d%d%d",&ff,&rr,&k);
printf("%d\n",query(rt[ff-1],rt[rr],k,1,n));
}
return 0;
}
带修改的主席树
待填坑