线段树
NOIP阶段只要不涉及区间翻转、区间插入的序列问题都可以用线段树实现。
每一个节点维护一个区间的信息。
线段树有分治的感觉。
线段树可以处理很多符合结合律的操作。
时间复杂度,建树为O(n),区间查询和区间修改为O(log n)。
空间要开原序列长度的四倍。
查询的话就是序列分割,由于分割次数近似log n,故总复杂度为O(n log n)
code:
void pushup(int cur)
{
val[cur]=val[ls[cur]]+val[rs[cur]];
}
void pushdown(int cur,int l,int r)
{
if(!lazy[cur]) return;
lazy[ls[cur]]+=lazy[cur];
lazy[rs[cur]]+=lazy[cur];
int mid=(l+r)>>1;
val[ls[cur]]+=lazy[cur]*(mid-l+1);
val[rs[cur]]+=lazy[cur]*(r-mid);
lazy[cur]=0;
}
void build(int L,int R,int &cur)
{
cur=++tree_cnt;
if(L==R)
{
val[cur]=a[L];
return;
}
int mid=(L+R)>>1;
build(L,mid,ls[cur]);
build(mid+1,R,rs[cur]);
pushup(cur);
}
void modify(int L,int R,int l,int r,ll add,int cur)
{
if(L<=l&&R>=r)
{
val[cur]+=add*(r-l+1);
lazy[cur]+=add;
return;
}
pushdown(cur,l,r);
int mid=(l+r)>>1;
if(L<=mid) modify(L,R,l,mid,add,ls[cur]);
if(R>mid) modify(L,R,mid+1,r,add,rs[cur]);
pushup(cur);
}
ll query(int L,int R,int l,int r,int cur)
{
if(L<=l&&R>=r) return val[cur];
pushdown(cur,l,r);
ll sum=0;
int mid=(l+r)>>1;
if(L<=mid) sum+=query(L,R,l,mid,ls[cur]);
if(R>mid) sum+=query(L,R,mid+1,r,rs[cur]);
return sum;
}
......
build(1,n,root);//建树
modify(x,y,1,n,z,root);//区间修改,x到y的区间加上z
query(x,y,1,n,root)//区间查询,x到y的和
动态开点
code:
void pushup(int cur)
{
val[cur]=val[ls[cur]]+val[rs[cur]];
}
void pushdown(int cur,int l,int r)
{
if(!lazy[cur]) return;
int mid=(l+r)>>1;
if(!ls[cur]) ls[cur]=++tree_cnt;
lazy[ls[cur]]+=lazy[cur];
val[ls[cur]]+=lazy[cur]*(mid-l+1);
if(!rs[cur]) rs[cur]=++tree_cnt;
lazy[rs[cur]]+=lazy[cur];
val[rs[cur]]+=lazy[cur]*(r-mid);
lazy[cur]=0;
}
void modify(int L,int R,int l,int r,ll add,int &cur)
{
if(!cur) cur=++tree_cnt;
if(L<=l&&R>=r)
{
val[cur]+=add*(r-l+1);
lazy[cur]+=add;
return;
}
pushdown(cur,l,r);
int mid=(l+r)>>1;
if(L<=mid) modify(L,R,l,mid,add,ls[cur]);
if(R>mid) modify(L,R,mid+1,r,add,rs[cur]);
pushup(cur);
}
ll query(int L,int R,int l,int r,int cur)
{
if(L<=l&&R>=r) return val[cur];
pushdown(cur,l,r);
ll sum=0;
int mid=(l+r)>>1;
if(L<=mid) sum+=query(L,R,l,mid,ls[cur]);
if(R>mid) sum+=query(L,R,mid+1,r,rs[cur]);
return sum;
}
线段树合并
将x和y合并为x
通常用来合并权值线段树(一个节点权值为其值域区间元素个数)
更新信息时要特判叶子节点的情况,防止信息覆盖,代码为雨天的尾巴
code:
int merge(int x,int y,int l,int r)
{
if(!x||!y) return x+y;
int p=++tree_cnt;
if(l==r) ma[p]=ma[x]+ma[y],id[p]=l;
else
{
ls[p]=merge(ls[x],ls[y],l,mid);
rs[p]=merge(rs[x],rs[y],mid+1,r);
pushup(p);
}
return p;
}
线段树分裂
code:
void pushup(int cur)
{
cnt[cur]=cnt[ls[cur]]+cnt[rs[cur]];
}
void modify(int l,int r,int pos,int v,int &cur)
{
if(!cur) cur=++tot;
if(l==r)
{
cnt[cur]+=v;
return;
}
if(pos<=mid) modify(l,mid,pos,v,ls[cur]);
else modify(mid+1,r,pos,v,rs[cur]);
pushup(cur);
}
int query(int L,int R,int l,int r,int cur)
{
if(!cur) return 0;
if(L<=l&&R>=r) return cnt[cur];
int v=0;
if(L<=mid) v+=query(L,R,l,mid,ls[cur]);
if(R>mid) v+=query(L,R,mid+1,r,rs[cur]);
return v;
}
int kth(int l,int r,int k,int cur)
{
if(l==r) return l;
if(cnt[ls[cur]]>=k) return kth(l,mid,k,ls[cur]);
else return kth(mid+1,r,k-cnt[ls[cur]],rs[cur]);
}
int merge(int x,int y)
{
if(!x||!y) return x+y;
int p=++tot;
cnt[p]=cnt[x]+cnt[y];
ls[p]=merge(ls[x],ls[y]);
rs[p]=merge(rs[x],rs[y]);
return p;
}
void split(int L,int R,int l,int r,int &x,int &y)
{
if(!x) return;
if(L<=l&&R>=r)
{
y=x,x=0;
return;
}
y=++tot;
if(L<=mid) split(L,R,l,mid,ls[x],ls[y]);
if(R>mid) split(L,R,mid+1,r,rs[x],rs[y]);
pushup(x),pushup(y);
}
线段树优化建图
code:
void build_in(int L,int R,int &cur)
{
cur=++tree_cnt;
if(L==R)
{
in_num[L]=cur;
return;
}
int mid=(L+R)>>1;
build_in(L,mid,ls[cur]);
build_in(mid+1,R,rs[cur]);
add(ls[cur],cur,0),add(rs[cur],cur,0);
}
void build_out(int L,int R,int &cur)
{
cur=++tree_cnt;
if(L==R)
{
out_num[L]=cur;
return;
}
int mid=(L+R)>>1;
build_out(L,mid,ls[cur]);
build_out(mid+1,R,rs[cur]);
add(cur,ls[cur],0),add(cur,rs[cur],0);
}
void modify_in(int L,int R,int l,int r,int pos,int val,int &cur)
{
if(L<=l&&R>=r)
{
add(cur,pos,val);
return;
}
int mid=(l+r)>>1;
if(L<=mid) modify_in(L,R,l,mid,pos,val,ls[cur]);
if(R>mid) modify_in(L,R,mid+1,r,pos,val,rs[cur]);
}
void modify_out(int L,int R,int l,int r,int pos,int val,int &cur)
{
if(L<=l&&R>=r)
{
add(pos,cur,val);
return;
}
int mid=(l+r)>>1;
if(L<=mid) modify_out(L,R,l,mid,pos,val,ls[cur]);
if(R>mid) modify_out(L,R,mid+1,r,pos,val,rs[cur]);
}
线段树分治
维护时间区间,对询问的时间轴进行分治,每个修改操作会有一个作用的区间,将修改标记到线段树上,dfs一遍线段树来统计答案
连通图:删去无向连通图一些边后,询问原图是否连通
将删边转化为边存在的区间,用带权并查集维护连通块大小
code:
void build(int l,int r,int &cur)
{
if(!cur) cur=++tree_cnt;
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,ls[cur]);
build(mid+1,r,rs[cur]);
}
void insert(int L,int R,int l,int r,int id,int cur)
{
if(L<=l&&R>=r)
{
v[cur].push_back(id);
return;
}
int mid=(l+r)>>1;
if(L<=mid) insert(L,R,l,mid,id,ls[cur]);
if(R>mid) insert(L,R,mid+1,r,id,rs[cur]);
}
int find(int x)
{
return fa[x]==x?x:find(fa[x]);
}
void merge(int x,int y)
{
int rx=find(x),ry=find(y);
if(rx==ry) return;
if(de[rx]<de[ry]) swap(rx,ry);
st[++top]=(node){rx,ry,de[rx]};
fa[ry]=rx,siz[rx]+=siz[ry];
de[rx]=max(de[rx],de[ry]+1);
}
void del(int id)
{
int x=st[id].x,y=st[id].y;
fa[y]=y,siz[x]-=siz[y],de[x]=st[id].deep;
}
void dfs(int l,int r,int cur)
{
int now=top,size=v[cur].size();
for(int i=0;i<size;++i)
merge(e[v[cur][i]].x,e[v[cur][i]].y);
if(l==r) ans[l]=(siz[find(1)]==n);
else
{
int mid=(l+r)>>1;
dfs(l,mid,ls[cur]),dfs(mid+1,r,rs[cur]);
}
while(top>now) del(top--);
}
Shortest Path Queries:维护一个无向连通图,边有边权,支持加边删边和询问从x到y的异或最短路
code:
void build(int l,int r,int &cur)
{
if(!cur) cur=++tree_cnt;
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,ls[cur]);
build(mid+1,r,rs[cur]);
}
void insert(int L,int R,int l,int r,int id,int cur)
{
if(L<=l&&R>=r)
{
v[cur].push_back(id);
return;
}
int mid=(l+r)>>1;
if(L<=mid) insert(L,R,l,mid,id,ls[cur]);
if(R>mid) insert(L,R,mid+1,r,id,rs[cur]);
}
void ins(int x,int cur)
{
for(int i=30;i>=0;--i)
{
if(x&(1<<i))
{
if(!a[cur][i])
{
a[cur][i]=x;
break;
}
else x^=a[cur][i];
}
}
}
int get(int x,int cur)
{
for(int i=30;i>=0;--i)
if((x^a[cur][i])<x)
x^=a[cur][i];
return x;
}
int find(int x)
{
return fa[x]==x?x:find(fa[x]);
}
int xor_dis(int x)
{
return fa[x]==x?dis[x]:dis[x]^xor_dis(fa[x]);
}
void merge(int x,int y,int v)
{
if(de[x]<de[y]) swap(x,y);
st[++top]=(node){x,y,de[x]};
fa[y]=x,dis[y]=v,de[x]=max(de[x],de[y]+1);
}
void del(int id)
{
int x=st[id].x,y=st[id].y;
fa[y]=y,dis[y]=0,de[x]=st[id].deep;
}
void copy(int x,int y)
{
for(int i=0;i<=30;++i) a[y][i]=a[x][i];
}
void dfs(int l,int r,int cur)
{
int now=top,size=v[cur].size();
for(int i=0;i<size;++i)
{
int id=v[cur][i],x=e[id].x,y=e[id].y,v=e[id].v;
v^=xor_dis(x)^xor_dis(y),x=find(x),y=find(y);
if(x==y) ins(v,cur);
else merge(x,y,v);
}
if(l==r)
{
int x=qu[l].x,y=qu[l].y;
ans[l]=get(xor_dis(x)^xor_dis(y),cur);
}
else
{
int mid=(l+r)>>1;
copy(cur,ls[cur]),dfs(l,mid,ls[cur]);
copy(cur,rs[cur]),dfs(mid+1,r,rs[cur]);
}
while(top>now) del(top--);
}
李超线段树
用来维护坐标系上的线段,支持添加线段和询问
每个线段树上每个区间维护一个优势线段,其至少覆盖了该区间的一半,维护时分类讨论即可
code:
double y(int id,int x)
{
return k[id]*(x-1)+b[id];
}
void modify(int l,int r,int id,int &cur)
{
if(!cur) cur=++tree_cnt;
if(y(id,l)>y(val[cur],l)&&y(id,r)>y(val[cur],r))
{
val[cur]=id;
return;
}
if(y(id,l)<=y(val[cur],l)&&y(id,r)<=y(val[cur],r)) return;
int mid=(l+r)>>1;
if(k[id]>k[val[cur]])
{
if(y(id,mid)>y(val[cur],mid))
modify(l,mid,val[cur],ls[cur]),val[cur]=id;
else modify(mid+1,r,id,rs[cur]);
}
else
{
if(y(id,mid)>y(val[cur],mid))
modify(mid+1,r,val[cur],rs[cur]),val[cur]=id;
else modify(l,mid,id,ls[cur]);
}
}
double query(int l,int r,int x,int cur)
{
if(l==r) return y(val[cur],x);
int mid=(l+r)>>1;
double ans=y(val[cur],x);
if(x<=mid) return max(ans,query(l,mid,x,ls[cur]));
else return max(ans,query(mid+1,r,x,rs[cur]));
}
code:
double y(int id,int x)
{
return k[id]*x+b[id];
}
bool check(int a,int b,int x)
{
return fabs(y(a,x)-y(b,x))>eps?y(a,x)<y(b,x):a>b;
}
void modify(int L,int R,int l,int r,int id,int &cur)
{
if(!cur) cur=++tree_cnt;
int mid=(l+r)>>1;
if(L<=l&&R>=r)
{
if(check(id,val[cur],l)&&check(id,val[cur],r)) return;
if(!check(id,val[cur],l)&&!check(id,val[cur],r))
{
val[cur]=id;
return;
}
if(!check(id,val[cur],mid)) swap(id,val[cur]);
if(!check(id,val[cur],l)) modify(L,R,l,mid,id,ls[cur]);
else modify(L,R,mid+1,r,id,rs[cur]);
return;
}
if(L<=mid) modify(L,R,l,mid,id,ls[cur]);
if(R>mid) modify(L,R,mid+1,r,id,rs[cur]);
}
int query(int l,int r,int x,int cur)
{
if(l==r) return val[cur];
int mid=(l+r)>>1,ans;
if(x<=mid) ans=query(l,mid,x,ls[cur]);
else ans=query(mid+1,r,x,rs[cur]);
if(check(ans,val[cur],x)) ans=val[cur];
return ans;
}
吉司机线段树
code:
void pushup(int cur)
{
sum[cur]=sum[ls]+sum[rs],ma[cur]=max(ma[ls],ma[rs]),hma[cur]=max(hma[ls],hma[rs]);
if(ma[ls]==ma[rs]) se[cur]=max(se[ls],se[rs]),cnt[cur]=cnt[ls]+cnt[rs];
else if(ma[ls]>ma[rs]) se[cur]=max(se[ls],ma[rs]),cnt[cur]=cnt[ls];
else se[cur]=max(ma[ls],se[rs]),cnt[cur]=cnt[rs];
}
void pushtag(int cur,int l,int r,ll v,ll hv,ll w,ll hw)
{
sum[cur]+=v*cnt[cur]+w*(r-l+1-cnt[cur]);
hma[cur]=max(hma[cur],ma[cur]+hv),ma[cur]+=v;
htag[cur]=max(htag[cur],tag[cur]+hv),tag[cur]+=v;
hadd[cur]=max(hadd[cur],add[cur]+hw),add[cur]+=w;
if(se[cur]!=-inf) se[cur]+=w;
}
void pushdown(int cur,int l,int r)
{
ll maxv=max(ma[ls],ma[rs]);
if(ma[ls]==maxv) pushtag(ls,l,mid,tag[cur],htag[cur],add[cur],hadd[cur]);
else pushtag(ls,l,mid,add[cur],hadd[cur],add[cur],hadd[cur]);
if(ma[rs]==maxv) pushtag(rs,mid+1,r,tag[cur],htag[cur],add[cur],hadd[cur]);
else pushtag(rs,mid+1,r,add[cur],hadd[cur],add[cur],hadd[cur]);
tag[cur]=htag[cur]=add[cur]=hadd[cur]=0;
}
void build(int l,int r,int cur)
{
if(l==r)
{
sum[cur]=ma[cur]=hma[cur]=a[l],se[cur]=-inf,cnt[cur]=1;
return;
}
build(l,mid,ls),build(mid+1,r,rs),pushup(cur);
}
void modify_add(int L,int R,int l,int r,ll v,int cur)
{
if(L<=l&&R>=r)
{
pushtag(cur,l,r,v,v,v,v);
return;
}
pushdown(cur,l,r);
if(L<=mid) modify_add(L,R,l,mid,v,ls);
if(R>mid) modify_add(L,R,mid+1,r,v,rs);
pushup(cur);
}
void modify_min(int L,int R,int l,int r,ll v,int cur)
{
if(v>=ma[cur]) return;
if(L<=l&&R>=r&&v>se[cur])
{
pushtag(cur,l,r,v-ma[cur],v-ma[cur],0,0);
return;
}
pushdown(cur,l,r);
if(L<=mid) modify_min(L,R,l,mid,v,ls);
if(R>mid) modify_min(L,R,mid+1,r,v,rs);
pushup(cur);
}
ll query_sum(int L,int R,int l,int r,int cur)
{
if(L<=l&&R>=r) return sum[cur];
pushdown(cur,l,r);
ll ans=0;
if(L<=mid) ans+=query_sum(L,R,l,mid,ls);
if(R>mid) ans+=query_sum(L,R,mid+1,r,rs);
return ans;
}
ll query_max(int L,int R,int l,int r,int cur)
{
if(L<=l&&R>=r) return ma[cur];
pushdown(cur,l,r);
ll ans=-inf;
if(L<=mid) ans=max(ans,query_max(L,R,l,mid,ls));
if(R>mid) ans=max(ans,query_max(L,R,mid+1,r,rs));
return ans;
}
ll query_his(int L,int R,int l,int r,int cur)
{
if(L<=l&&R>=r) return hma[cur];
pushdown(cur,l,r);
ll ans=-inf;
if(L<=mid) ans=max(ans,query_his(L,R,l,mid,ls));
if(R>mid) ans=max(ans,query_his(L,R,mid+1,r,rs));
return ans;
}
code:
void pushup(int cur)
{
ma[cur]=max(ma[ls],ma[rs]),hma[cur]=max(hma[ls],hma[rs]);
}
void pushadd(int cur,ll v,ll hv)
{
hma[cur]=max(hma[cur],ma[cur]+hv),ma[cur]+=v;
if(!tag[cur]) hadd[cur]=max(hadd[cur],add[cur]+hv),add[cur]+=v;
else hcov[cur]=max(hcov[cur],cov[cur]+hv),cov[cur]+=v;
}
void pushcov(int cur,ll v,ll hv)
{
tag[cur]=1;
hma[cur]=max(hma[cur],hv),ma[cur]=v;
hcov[cur]=max(hcov[cur],hv),cov[cur]=v;
}
void pushdown(int cur)
{
if(add[cur]) pushadd(ls,add[cur],hadd[cur]),pushadd(rs,add[cur],hadd[cur]);
if(tag[cur]) pushcov(ls,cov[cur],hcov[cur]),pushcov(rs,cov[cur],hcov[cur]);
add[cur]=hadd[cur]=tag[cur]=cov[cur]=hcov[cur]=0;
}
void build(int l,int r,int cur)
{
if(l==r)
{
ma[cur]=hma[cur]=a[l];
return;
}
build(l,mid,ls),build(mid+1,r,rs),pushup(cur);
}
void modify_add(int L,int R,int l,int r,ll v,int cur)
{
if(L<=l&&R>=r)
{
pushadd(cur,v,v);
return;
}
pushdown(cur);
if(L<=mid) modify_add(L,R,l,mid,v,ls);
if(R>mid) modify_add(L,R,mid+1,r,v,rs);
pushup(cur);
}
void modify_cov(int L,int R,int l,int r,ll v,int cur)
{
if(L<=l&&R>=r)
{
pushcov(cur,v,v);
return;
}
pushdown(cur);
if(L<=mid) modify_cov(L,R,l,mid,v,ls);
if(R>mid) modify_cov(L,R,mid+1,r,v,rs);
pushup(cur);
}
ll query_max(int L,int R,int l,int r,int cur)
{
if(L<=l&&R>=r) return ma[cur];
pushdown(cur);
ll ans=-inf;
if(L<=mid) ans=max(ans,query_max(L,R,l,mid,ls));
if(R>mid) ans=max(ans,query_max(L,R,mid+1,r,rs));
return ans;
}
ll query_his(int L,int R,int l,int r,int cur)
{
if(L<=l&&R>=r) return hma[cur];
pushdown(cur);
ll ans=-inf;
if(L<=mid) ans=max(ans,query_his(L,R,l,mid,ls));
if(R>mid) ans=max(ans,query_his(L,R,mid+1,r,rs));
return ans;
}
__EOF__

本文作者:lhm_
本文链接:https://www.cnblogs.com/lhm-/p/12229433.html
关于博主:sjzez 的一名 OI 学生
版权声明:转载标明出处
声援博主:希望得到宝贵的建议
本文链接:https://www.cnblogs.com/lhm-/p/12229433.html
关于博主:sjzez 的一名 OI 学生
版权声明:转载标明出处
声援博主:希望得到宝贵的建议
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现