抱怨

真不应该直接去刚​​(主席树:可持久化线段树)​​,从中午12点学晚上到快7点,才理解,这不是我能刚的动的,为什么?因为学主席树之前要先学权值线段树,如果不了解权值线段树,直接刚主席树,根本不知道在说个啥玩意,效率很低

现在我讲一下权值线段树:

给一串数 ​​1,2,3,1,5,4​​​ 很明显这串数的值域是​​[1,5]​​​,我们把值域二分为区间,求每一个区间有多少数,然后可以以通过这种方案来求:第​​k​​​大或者第​​k​​小的数

第一步:

直接建好一个空树
void Set(int k,int l,int r)//建一个空树
{
val_tree[k].l=l;
val_tree[k].r=r;
val_tree[k].sum=0;
if(l==r)
{
return;
}
int mid=(l+r)>>1;
Set(k<<1,l,mid);
Set(k<<1|1,mid+1,r);
}

第二步:

更新区间中值的个数
void update(int k,int pos,int w)//更新
{
if(val_tree[k].l==val_tree[k].r)
{
val_tree[k].sum+=w;
return ;
}
int mid=(val_tree[k].l+val_tree[k].r)>>1;
if(mid>=pos)
{
update(k<<1,pos,w);
}
else
{
update(k<<1|1,pos,w);
}
push_up(k);
}

第四步

查询不同区间个数的数量
int query_num(int k,int l,int r)
{
if(val_tree[k].l>=l&&val_tree[k].r<=r)
{
return val_tree[k].sum;
}
int mid=(val_tree[k].l+val_tree[k].r)>>1;
if(mid>=r)
{
return query_num(k<<1,l,r);
}
else if(mid<l)
{
return query_num(k<<1|1,l,r);
}
return query_num(k<<1,l,mid)+query_num(k<<1|1,mid+1,r);

}

权值线段树+二分离散化: 完整求不同值域区间含有数的数量

#include<bits/stdc++.h>
using namespace std;
//权值树
#define maxn 1005
struct node
{
int l,r,sum;
} val_tree[maxn*40];
int a[maxn];
//离散化
vector<int>vec;
int getid(int x)
{
return lower_bound(vec.begin(),vec.end(),x)-vec.begin()+1;
}
void push_up(int k)//维护区间
{
val_tree[k].sum=val_tree[k<<1].sum+val_tree[k<<1|1].sum;
}
void Set(int k,int l,int r)//建一个空树
{
val_tree[k].l=l;
val_tree[k].r=r;
val_tree[k].sum=0;
if(l==r)
{
return;
}
int mid=(l+r)>>1;
Set(k<<1,l,mid);
Set(k<<1|1,mid+1,r);
}
void update(int k,int pos,int w)//更新
{
if(val_tree[k].l==val_tree[k].r)
{
val_tree[k].sum+=w;
return ;
}
int mid=(val_tree[k].l+val_tree[k].r)>>1;
if(mid>=pos)
{
update(k<<1,pos,w);
}
else
{
update(k<<1|1,pos,w);
}
push_up(k);
}
int query_num(int k,int l,int r)
{
if(val_tree[k].l>=l&&val_tree[k].r<=r)
{
return val_tree[k].sum;
}
int mid=(val_tree[k].l+val_tree[k].r)>>1;
if(mid>=r)
{
return query_num(k<<1,l,r);
}
else if(mid<l)
{
return query_num(k<<1|1,l,r);
}
return query_num(k<<1,l,mid)+query_num(k<<1|1,mid+1,r);

}
int main()
{
int n,x;
scanf("%d",&n);

for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
vec.push_back(a[i]);
}
sort(vec.begin(),vec.end());
vec.erase(unique(vec.begin(),vec.end()),vec.end());//离散化
//printf("%d\n",vec.size());
Set(1,1,vec.size());//值域1---n
for(int i=1; i<=n; i++)
{
update(1,getid(a[i]),1);
}
int l,r;
while(1)
{
cin>>l>>r;
cout<<query_num(1,l,r);
}
}

上面可能讲的不太细致,现在我们模拟一下过程来进行权值线段树讲解:

举一个栗子:假如我们要求 ​​1 2 2 3 4 6​​​ 整个区间的第​​k​​​大,可以进行修改某个点。
这里说一下,为什么不用​​​sort()​​​ 假如我们可以询问 ​​n​​​次,每次询问分为查询和更改
如果用​​​sort()​​​写最坏情况时间复杂度 ​​O(n*nlogn)​​​,每次修改一次都需要排序,所以如果修改​​n-1​​​次查询​​1​​​次。呢么时间复杂度==​​O(n*nlogn)​​​,而权值线段树查第​​k​​​大的,只有O(n*logn),因为每次查询和更改的复杂度都是​​O(logn)​​​ 理解线段树之后,权值线段树其实就是把建树区间变为值域了,如果值域太大,而且数据量不是很大,呢么就离散化处理一下就ok了
离散化一下,当前的值域为​​1--n​​ 建一个树,记录所有区间有多少个数,然后根据左右区间的大小和数量关系求取第k大即可

这是求取区间最大和最小,用权值线段树写的:

#include<cstdio>
#include<algorithm>
using namespace std;
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
//求第k大
const int maxn=1e4+5;
int a[maxn];
vector<int>vec;
int getid(int x)//离散化
{
return lower_bound(vec.begin(),vec.end(),x)-vec.begin()+1;
}
struct node
{
int l,r,sum;
} tree[maxn*4];
void pushup(int k)
{
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}
void build(int k,int l,int r)
{
tree[k].l=l;
tree[k].r=r;
tree[k].sum=0;
if(l==r)
{
return ;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
void update(int k,int pos,int w)
{
if(tree[k].l==tree[k].r)
{
tree[k].sum+=w;
return ;
}
int mid=(tree[k].l+tree[k].r)>>1;
if(mid>=pos)
update(k<<1,pos,w);
else
update(k<<1|1,pos,w);
pushup(k);
}
int query_kth(int k,int ik)
{
if(tree[k].l==tree[k].r)
{
return tree[k].l;
}
if(tree[k<<1].sum>=ik)
return query_kth(k<<1,ik);
else
return query_kth(k<<1|1,ik-tree[k<<1].sum);
}
int main()
{

int n=5;
//scanf("%d",&n);
for(int i=1;i<=5;i++)
{
scanf("%d",&a[i]);
vec.push_back(a[i]);
}
sort(vec.begin(),vec.end());
vec.erase(unique(vec.begin(),vec.end()),vec.end());
build(1,1,vec.size());
for(int i=1;i<=n;i++)
{
update(1,getid(a[i]),1);
}

int ans2=vec[query_kth(1,n)-1];
int ans1=vec[query_kth(1,1)-1];
printf("%d %d\n",ans1,ans2);
}