LGP5494 [LG TPLT] 线段树分裂 学习笔记

LGP5494 [LG TPLT] 线段树分裂 学习笔记

\(\texttt{Luogu Link}\)

前言

\(\texttt{Q:}\) 有什么数据结构是支持用合并&分裂查询答案信息的呢?
\(\texttt{A:}\) \(\text{FHQ-Treap}\)
\(\texttt{Q:}\) 还有吗?
\(\texttt{A:}\) 线段树。

—— \(\texttt{cyffff}\)

所以,来都来了,练了线段树合并,就也写一下线段树分裂吧。

因为实际上,一个能合并能分裂的 \(\text{SegTrees}\) 在功能上与一棵 \(\text{FHQ-Treap}\) 确实就相差的不太多了(来源请求)。

题意简述

给出一个可重集,编号为 \(1\),请支持以下五种操作:

  • 0 p x y:将 \(p\) 号可重集中所有 \([x,y]\) 范围的值移到一个新可重集(其编号等于之前生成过的可重集数量加一)中。
  • 1 p t:将 \(t\) 号可重集中的所有数放入 \(p\) 号可重集中,删除 \(t\)
  • 2 p x k:往 \(p\) 号可重集中加入 \(x\) 个数字 \(k\)
  • 3 p x y:查询 \(p\) 号可重集中值域在 \([x,y]\) 范围的数的个数。
  • 4 p k:查询 \(p\) 号可重集中第 \(k\) 小的数,若不存在则输出 -1

\(n,x,y,k,q,m\le 2\times 10^5\)。保证数据合法。

做法解析

如果没有 \(0\) 操作就是线段树合并(或平衡树)板子。所以这里只讲解 \(0\) 操作。

其实也很简单。我们把一只线段树按值域分成两半是很容易的,那把 \([x,y]\)split 出去也不难,就是劈两刀再把两边的拼回去。

哦为了空间不爆炸,记得回收用完的结点。

直接看代码吧。

代码实现

#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=2e5+5;
int N,M,X,Opt,Y,Z;
int srt[MaxN],tcnt;
struct SegTrees{
    lolo sum[MaxN<<5];
    int ls[MaxN<<5],rs[MaxN<<5],tot;
    int pol[MaxN<<5],pcnt;
    void init(int n){for(int i=1;i<=n;i++)srt[i]=++tot;}
    int newnode(){return pcnt?pol[pcnt--]:++tot;}
    void delnode(int u){pol[++pcnt]=u,ls[u]=rs[u]=sum[u]=0;}
    void pushup(int u){sum[u]=sum[ls[u]]+sum[rs[u]];}
    void modify(int &u,int cl,int cr,int dd,int x){
        if(!u)u=newnode();
        if(cl==cr){sum[u]+=x;return;}
        int cmid=(cl+cr)>>1;
        if(dd<=cmid)modify(ls[u],cl,cmid,dd,x);
        else modify(rs[u],cmid+1,cr,dd,x);
        pushup(u);
    }
    lolo getsum(int u,int cl,int cr,int dl,int dr){
        if(dl<=cl&&cr<=dr)return sum[u];
        int cmid=(cl+cr)>>1;lolo res=0;
        if(dl<=cmid)res+=getsum(ls[u],cl,cmid,dl,dr);
        if(dr>cmid)res+=getsum(rs[u],cmid+1,cr,dl,dr);
        return res;
    }
    int qkth(int u,int cl,int cr,lolo k){
        if(cl==cr)return cl;
        int cmid=(cl+cr)>>1;
        if(sum[ls[u]]>=k)return qkth(ls[u],cl,cmid,k);
        else return qkth(rs[u],cmid+1,cr,k-sum[ls[u]]);
    }
    int merge(int u,int v){
        if(!u||!v)return u|v;
        sum[u]+=sum[v];
        ls[u]=merge(ls[u],ls[v]);
        rs[u]=merge(rs[u],rs[v]);
        delnode(v);return u;
    }
    void split(int u,int &v,lolo ck){
        if(!u)return;
        v=newnode();
        lolo lk=sum[ls[u]];
        if(ck>lk)split(rs[u],rs[v],ck-lk);
        else swap(rs[u],rs[v]);
        if(ck<lk)split(ls[u],ls[v],ck);
        sum[v]=sum[u]-ck,sum[u]=ck;
    }
}SgS;
int main(){
    readis(N,M);SgS.init(N),tcnt=1;
    for(int i=1;i<=N;i++)readi(X),SgS.modify(srt[1],1,N,i,X);
    for(int i=1;i<=M;i++){
        readi(Opt);
        if(Opt==0){
            readis(X,Y,Z);int tmp=0;
            lolo s1=Y>1?SgS.getsum(srt[X],1,N,1,Y-1):0;
            lolo s2=SgS.getsum(srt[X],1,N,Y,Z);
            SgS.split(srt[X],srt[++tcnt],s1);
            SgS.split(srt[tcnt],tmp,s2);
            srt[X]=SgS.merge(srt[X],tmp);
        }
        if(Opt==1)readis(X,Y),srt[X]=SgS.merge(srt[X],srt[Y]);
        if(Opt==2)readis(X,Y,Z),SgS.modify(srt[X],1,N,Z,Y);
        if(Opt==3)readis(X,Y,Z),writil(SgS.getsum(srt[X],1,N,Y,Z));
        if(Opt==4)readis(X,Y),writil(SgS.sum[srt[X]]>=Y?SgS.qkth(srt[X],1,N,Y):-1);
    }
    return 0;
}
posted @ 2025-10-22 20:15  矞龙OrinLoong  阅读(3)  评论(0)    收藏  举报