线段树合并&分裂
线段树合并&分裂
线段树合并
所谓线段树合并就是将两个线段树合并成一颗线段树的算法
il int merge(int rt1,int rt2,int l,int r){
if(!(rt1*rt2)) return rt1+rt2;
if(l==r) return t[rt1].sum+=t[rt2].sum,rt1;
t[rt1].ls=merge(t[rt1].ls,t[rt2].ls,l,mid);
t[rt1].rs=merge(t[rt1].rs,t[rt2].rs,mid+1,r);
return pushup(rt1),rt1;
}
code
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
using namespace std;
namespace Q{
il int rd(){
ri int x=0;ri bool f=0;ri char c=getchar();
while(!isdigit(c)) f|=(c=='-'),c=getchar();
while(isdigit(c))x=x*10+(c^48),c=getchar();
return f?-x:x;
}
il void wt(int x){
if(x<0) x=-x,putchar('-');
if(x>=10) wt(x/10);
return putchar(x%10+48),void();
}
}
cs int N=1e5;
int n,m;
namespace edge{
int h[N+1];
struct qwq{
int v,nxt;
}e[N<<1];
il void add(cs int u,cs int v,cs int i){
e[i*2-1]={v,h[u]},h[u]=i*2-1;
e[i<<1]={u,h[v]},h[v]=i<<1;
}
}
namespace LCA{
int f[N+1][20],dep[N+1];
il void dfs(int u){
dep[u]=dep[f[u][0]]+1;
for(ri int i=edge::h[u];i;i=edge::e[i].nxt){
if(edge::e[i].v!=f[u][0]){
f[edge::e[i].v][0]=u;
for(ri int l=1;l<20;++l){
f[edge::e[i].v][l]=f[f[edge::e[i].v][l-1]][l-1];
}
dfs(edge::e[i].v);
}
}
return;
}
il int lca(int u,int v){
if(dep[u]<dep[v]) swap(u,v);
for(ri int l=19;~l;--l){
if(dep[f[u][l]]>=dep[v]) u=f[u][l];
}
if(u==v) return u;
for(ri int l=19;~l;--l){
if(f[u][l]!=f[v][l]){
u=f[u][l],v=f[v][l];
}
}
return f[u][0];
}
}
namespace Tree{
struct tree{
int ls,rs,sum,num;
}t[N*80];
il void pushup(cs int rt){
if(!t[rt].ls){
t[rt].sum=t[t[rt].rs].sum;
t[rt].num=t[t[rt].rs].num;
}
else if(!t[rt].rs){
t[rt].sum=t[t[rt].ls].sum;
t[rt].num=t[t[rt].ls].num;
}
else if(t[t[rt].ls].sum>=t[t[rt].rs].sum){
t[rt].sum=t[t[rt].ls].sum;
t[rt].num=t[t[rt].ls].num;
}
else{
t[rt].sum=t[t[rt].rs].sum;
t[rt].num=t[t[rt].rs].num;
}
return;
}
int cnt;
#define mid ((l+r)>>1)
il void add(int &rt,int l,int r,int knd,int val){
if(!rt) rt=++cnt;
if(l==r) return t[rt].sum+=val,t[rt].num=knd,void();
if(knd<=mid) add(t[rt].ls,l,mid,knd,val);
else add(t[rt].rs,mid+1,r,knd,val);
return pushup(rt);
}
il int merge(int rt1,int rt2,int l,int r){
if(!(rt1*rt2)) return rt1+rt2;
if(l==r) return t[rt1].sum+=t[rt2].sum,rt1;
t[rt1].ls=merge(t[rt1].ls,t[rt2].ls,l,mid);
t[rt1].rs=merge(t[rt1].rs,t[rt2].rs,mid+1,r);
return pushup(rt1),rt1;
}
int rot[N+1],as[N+1];
il void sol(int u){
for(ri int i=edge::h[u],v;i;i=edge::e[i].nxt){
if((v=edge::e[i].v)!=LCA::f[u][0]){
sol(v),rot[u]=Tree::merge(rot[u],rot[v],1,N);
}
}
as[u]=Tree::t[rot[u]].num;
if(!Tree::t[rot[u]].sum) as[u]=0;
return;
}
}
signed main(){
n=Q::rd(),m=Q::rd();
for(ri int i=1;i<n;++i){
edge::add(Q::rd(),Q::rd(),i);
}
LCA::dfs(1);
for(ri int i=1,u,v,num,l;i<=m;++i){
u=Q::rd(),v=Q::rd(),num=Q::rd();
Tree::add(Tree::rot[u],1,N,num,1);
Tree::add(Tree::rot[v],1,N,num,1);
l=LCA::lca(u,v),Tree::add(Tree::rot[l],1,N,num,-1);//N不是n
Tree::add(Tree::rot[LCA::f[l][0]],1,N,num,-1);
//是rot[u],不是u,每个节点建一颗树!!!
}
Tree::sol(1);
for(ri int i=1;i<=n;++i){
Q::wt(Tree::as[i]),putchar('\n');
}
return 0;
}
线段树分裂
分裂的主要思路在于动态开点和值的转移,将原值附在新树上同时清空原点(不是删除)。
il int split(int pre,int l,int r,int ql,int qr){
ri int rt=++id;
if(ql<=l&&r<=qr) t[rt]=t[pre],t[pre]=(tr){0,0,0};
else{
if(mid>=qr) ls=split(t[pre].Ls,l,mid,ql,qr);
else if(mid<ql) rs=split(t[pre].Rs,mid+1,r,ql,qr);
else{//
ls=split(t[pre].Ls,l,mid,ql,mid);
rs=split(t[pre].Rs,mid+1,r,mid+1,qr);
}
pushup(rt),pushup(pre);
}
return rt;
}
code
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
#define int long long
using namespace std;
namespace Q{
il int rd(){
ri int x=0;ri bool f=0;ri char c=getchar();
while(!isdigit(c)) f|=(c==45),c=getchar();
while(isdigit(c)) x=x*10+(c^48),c=getchar();
return f?-x:x;
}
short a[30],tl;
il void wt(int x){
if(x<0) x=-x,putchar(45);
do a[++tl]=x%10,x/=10; while(x);
while(tl) putchar(a[tl--]|48);
return putchar(10),void();
}
} using namespace Q;
cs int N=2e5+1;
namespace tree{
#define ls t[rt].Ls
#define rs t[rt].Rs
#define mid ((l+r)>>1)
int id,rot[N],tot;
struct tr{
int sum,Ls,Rs;
}t[N<<5];
il void pushup(int rt){
t[rt].sum=t[ls].sum+t[rs].sum;
return;
}
il int build(int l,int r){
ri int rt=++id;
if(l==r) t[rt].sum=rd();
else{
ls=build(l,mid);
rs=build(mid+1,r);
pushup(rt);
}
return rt;
}
il int split(int pre,int l,int r,int ql,int qr){
ri int rt=++id;
if(ql<=l&&r<=qr) t[rt]=t[pre],t[pre]=(tr){0,0,0};
else{
if(mid>=qr) ls=split(t[pre].Ls,l,mid,ql,qr);
else if(mid<ql) rs=split(t[pre].Rs,mid+1,r,ql,qr);
else{//
ls=split(t[pre].Ls,l,mid,ql,mid);
rs=split(t[pre].Rs,mid+1,r,mid+1,qr);
}
pushup(rt),pushup(pre);
}
return rt;
}
il int merge(int rt,int pre){
if(!(rt*pre)) return rt+pre;
t[rt].sum+=t[pre].sum;
ls=merge(ls,t[pre].Ls);
rs=merge(rs,t[pre].Rs);
return rt;
}
il void add(int &rt,int l,int r,int pos,int k){
if(!rt) rt=++id; t[rt].sum+=k;
if(l^r){
if(mid>=pos) add(ls,l,mid,pos,k);
else add(rs,mid+1,r,pos,k);
}
return;
}
il int ask(int rt,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr) return t[rt].sum;
ri int as=0;
if(mid>=ql) as+=ask(ls,l,mid,ql,qr);
if(mid<qr) as+=ask(rs,mid+1,r,ql,qr);
return as;
}
il int kth(int rt,int l,int r,int k){
if(l==r) return l;
if(t[ls].sum>=k) return kth(ls,l,mid,k);
else return kth(rs,mid+1,r,k-t[ls].sum);
}
il int siz(int p,int n){
return ask(rot[p],1,n,1,n);
}
#undef ls
#undef rs
#undef mid
} using namespace tree;
signed main(){
ri int n=rd(),m=rd();rot[++tot]=build(1,n);
for(ri int i=1,op,p,x,y;i<=m;++i){
op=rd(),p=rd(),x=rd();
if(op==0) y=rd(),rot[++tot]=split(rot[p],1,n,x,y);
if(op==1) rot[p]=merge(rot[p],rot[x]);
if(op==2) y=rd(),add(rot[p],1,n,y,x);
if(op==3) y=rd(),wt(ask(rot[p],1,n,x,y));
if(op==4) wt(siz(p,n)<x?-1:kth(rot[p],1,n,x));
}
return 0;
}
I went to the woods because I wanted to live deliberately, I wanted to live deep and suck out all the marrow of life, and not when I had come to die, discover that I had not live.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具