浅谈可持久化线段树

  • 维护历史值

    当要修改一个节点时,把跟他有关的线段树中所有节点舍弃,并建立新节点连接.

    img

    代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1e6+5;
    int n,m,a[N],root[N],top;
    struct node
    {
    	int l,r,val;
    }t[N*40];
    int clone(int x)//新建节点
    {
    	top++;
    	t[top]=t[x];
    	return top;
    }
    int build(int x,int l,int r)//建树
    {
    	x=++top;
    	if(l==r)
    	{
    		t[x].val=a[l];
    		return top;
    	}
    	int mid=(l+r)>>1;
    	t[x].l=build(t[x].l,l,mid);
    	t[x].r=build(t[x].r,mid+1,r);
    	return x;
    }
    int update(int u,int l,int r,int x,int val){
    	u=clone(u);
    	if(l==r){
    		t[u].val=val;
    		return u;
    	}
    	else{
    		int mid=(l+r)>>1;
    		if(x<=mid)
    		{
    			t[u].l=update(t[u].l,l,mid,x,val);
    		}
    		else
    		{
    			t[u].r=update(t[u].r,mid+1,r,x,val);
    		}
    	}
        return u;
    }
    int query(int u,int l,int r,int x)
    {
    	if(l==r){
    		return t[u].val;
    	}
    	else{
    		int mid=(l+r)>>1;
    		if(x<=mid)return query(t[u].l,l,mid,x);
    		else return query(t[u].r,mid+1,r,x);
    	}
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    	root[0]=build(0,1,n);
    	for(int i=1,op,rt,x,y;i<=m;i++)
    	{
    		scanf("%d%d%d",&rt,&op,&x);
    		if(op==1)
    		{
    			scanf("%d",&y);
    			root[i]=update(root[rt],1,n,x,y);
    		}
    		else
    		{
    			printf("%d\n",query(root[rt],1,n,x));
    			root[i]=root[rt];
    		}
    	}
    	return 0;
    }
    
  • 求区间第k最值

    先离散化数组.

    再建立可持久化线段树,依次加入数组里每个数

    利用前缀和,求第k最值.

    #include <bits/stdc++.h>
    using namespace std;
    const int N=2e5+5;
    int n,ind[N],a[N],m,len,rt[N];
    int tot;
    int ls[N<<5],rs[N<<5],sum[N<<5];
    int getid(int x)
    {
    	return lower_bound(ind+1,ind+len+1,x)-ind;
    }
    int build(int l,int r)
    {
    	int root=++tot;
    	if(l==r)return root;
    	int mid=(l+r)>>1;
    	ls[root]=build(l,mid);
    	rs[root]=build(mid+1,r);
    	return root; 
    }
    int update(int k,int l,int r,int root)
    {
    	int dir=++tot;
    	ls[dir]=ls[root],rs[dir]=rs[root],sum[dir]=sum[root]+1;//多了一个数
    	if(l==r)
    	{
    		return dir;
    	}
    	int mid=(l+r)>>1;
    	if(k<=mid)
    	{
    		ls[dir]=update(k,l,mid,ls[dir]);
    	}
    	else
    	{
    		rs[dir]=update(k,mid+1,r,rs[dir]);
    	}
    	return dir;
    }
    int query(int u,int v,int l,int r,int k)
    {
    	int x=sum[ls[v]]-sum[ls[u]];//求左子树有多少种数
    	if(l==r)
    	{
    		return l;
    	}
    	int mid=(l+r)>>1;
    	if(k<=x)//在这个区间中,第k最值小于等于左子树数的总数
    	{
    		return query(ls[u],ls[v],l,mid,k);
    	}
    	else
    	{
    		return query(rs[u],rs[v],mid+1,r,k-x);
    	}
    }
    void init()
    {
    	cin>>n>>m;
    	for(int i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	memcpy(ind,a,sizeof(ind));
    	sort(ind+1,ind+n+1);
    	len=unique(ind+1,ind+n+1)-ind-1;//离散化
    	rt[0]=build(1,len);
    	for(int i=1;i<=n;i++)
    	{
    		rt[i]=update(getid(a[i]),1,len,rt[i-1]);
    	}
    }
    int l,r,k;
    void work()
    {
    	while(m--)
    	{
    		cin>>l>>r>>k;
    		cout<<ind[query(rt[l-1],rt[r],1,len,k)]<<'\n';
    	}
    }
    int main()
    {
    	init();
    	work();
    	return 0;
    }
    
posted @ 2024-08-16 19:08  tyccyt  阅读(6)  评论(0编辑  收藏  举报