主席树(可持久化权值线段树)模板学习笔记
碎碎念
其实主席树似乎是特指可持久化权值线段树,不知道能否指可持久化线段树呢///
主席树
给定 \(n\) 个整数构成的序列 \(a\),\(m\)次询问对于指定的闭区间 \([l, r]\) 查询其区间内的第 \(k\)小值。
\(n,m<=2e5,|a_i|<=1e9\)
可持久化线段树,保存每次插入时的历史版本。动态开点,每次保存新开的根节点编号\(root[N]\)和每个左右儿子编号\(lson,rson\)。
#define N 200005
#define lc(x) t[x].lc
#define rc(x) t[x].rc
int n,m,a[N];
vector<int> v;
struct seg{
int lc,rc,sum;
}t[N*22];
int root[N],idx;
build()
建空树。
void build(int &x,int l,int r){//传引用赋值
x=++idx;
if(l==r) return;
int m=l+r>>1;
build(lc(x),l,m);
build(rc(x),m+1,r);
}
insert()
建主席树,对每个版本依次插入,插入的基树建立在上一个版本上。
root[i]表示第i个版本的根节点编号,第i棵主席树实际上保存的是[1,i]区间所属的权值线段树。
void insert(int x,int &y,int l,int r,int v)
//y是目前要插入的版本的节点,x是上一个版本的同步节点。y的插入建立在x的基础上
//v是目前要插入的权值
{
y=++idx;t[y]=t[x];t[y].sum++;//先把y的左右儿子设置成与x相同,增加y区间权值的总数
if(l==r) return;
int m=l+r>>1;
if(v<=m) insert(lc(x),lc(y),l,m,v);//双指针同步搜索传引用,此时会新建y的左子节点
else insert(rc(x),rc(y),m+1,r,v);//此时会新建y的右子节点
}
//在主函数中
for(int i=1;i<=n;i++)
insert(root[i-1],root[i],1,n,a[i]);//在i-1版本的基础上插入i版本,权值为a[i]
query()
在主席树上查询[l,r]第k小。
[l,r]上的信息=[1,r]的信息-[1,l-1]的信息。
x为l-1版本指针,y为r版本指针。x,y同步搜索到达某区间,则[l,r]有多少个数字在此区间内=t[y].sum-t[x].sum。二分搜索找到第k个数字即可。
int query(int x,int y,int l,int r,int k)
{
if(l==r) return l;
int m=l+r>>1;
int sum=t[lc(y)].sum-t[lc(x)].sum; //x、y的左儿子有多少个数字
if(k<=sum) return query(lc(x),lc(y),l,m,k);
else return query(rc(x),rc(y),m+1,r,k-sum);
}
//在主函数中
cin>>l>>r>>k;
query(root[l-1],root[r],1,n,k);
离散化
n小值域大,考虑将数值映射为下标,需要离散化与去重
//vector的作用
for(int i=1;i<=n;i++)
{
cin>>a[i];v.push_back(a[i]);
}
//离散化
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
//unique将不相同的数字排到可变数组最前,并返回第一个重复数字的指针。erase将重复部分删掉。
//得到x的下标
int getid(int x)
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
//lower_bound返回第一个>=x的指针。
//1 1 2 3查询1,再减去v.begin()会得到0,所以要加上1
}
主函数
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
int vn=v.size();//1~vn即为离散化后的值域下标
for(int i=1;i<=n;i++)
insert(root[i-1],root[i],1,vn,getid(a[i]));//多次插入a[i]离散化后对应的下标即可
while(m--)
{
int l,r,k;cin>>l>>r>>k;
int id=query(root[l-1],root[r],1,vn,k)-1;
cout<<v[id]<<'\n';
}
}

浙公网安备 33010602011771号