线段树合集整理

编程入门 行业动态 更新时间:2024-10-09 05:17:55

<a href=https://www.elefans.com/category/jswz/34/1769188.html style=线段树合集整理"/>

线段树合集整理

  • 前言    

本章不是线段树教程,包含内容是博主自己刷题时的整理,请不要转载

 训练地址:一本通线段树地址  (剩余在每个板块都有链接

  • 单点修改,区间查询

没什么好说的,注意每次修改要记得pushup

附板子,0为单点修改,1为查询区间和

一般这样还是搞树状数组吧 这破玩意常数太大卡死了

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<iomanip>
using namespace std;
#define int long long
inline int read()
{int x=0,y=1; char c=getchar();while(c<'0'||c>'9') {if(c=='-') y=-1; c=getchar();}while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();return x*y;
}
const int maxn=100005;
int n,m;
int tree[maxn*4];
void push_up(int id)
{tree[id]=tree[id<<1]+tree[id<<1|1];return ;
}
void modify(int id,int l,int r,int pos,int x)
{if(l==r) {tree[id]+=x; return ;}int mid=(l+r)>>1;if(pos<=mid) modify(id<<1,l,mid,pos,x);else modify(id<<1|1,mid+1,r,pos,x);push_up(id);return ;
}
int query(int id,int l,int r,int x,int y)
{if(l>=x&&r<=y) return tree[id];int ans=0;int mid=(l+r)>>1;if(x<=mid) ans+=query(id<<1,l,mid,x,y);if(y>mid) ans+=query(id<<1|1,mid+1,r,x,y);return ans;
}
signed main()
{n=read(); m=read();while(m--){int op=read();int l=read(),r=read();if(!op) modify(1,1,n,l,r);else printf("%lld\n",query(1,1,n,l,r));}return 0;
}
  •  区间修改,区间查询

也是线段树基操, 附板子,C是修改区间,Q是查询

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<string>
#include<queue>
#include<iomanip>
using namespace std;
const int maxn=100005;
#define int long long
inline int read()
{int x=0,y=1; char c=getchar();while(c<'0'||c>'9') {if(c=='-') y=-1; c=getchar();}while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();return x*y;
}
int n,Q;
int a[maxn],tree[maxn*4];
int tag[maxn*4];
char op[2];
void push_up(int id)
{tree[id]=tree[id<<1]+tree[id<<1|1];return ;
}
void Add(int id,int l,int r,int val)
{tag[id]+=val;tree[id]+=(r-l+1)*val;return ;
}
void build(int id,int l,int r)
{if(l==r) {tree[id]=a[l]; return ;}int mid=(l+r)>>1;build(id<<1,l,mid);build(id<<1|1,mid+1,r);push_up(id);return ;
}
void push_down(int id,int l,int r)
{if(!tag[id]) return ;int mid=(l+r)>>1;Add(id<<1,l,mid,tag[id]);Add(id<<1|1,mid+1,r,tag[id]);tag[id]=0;return ;
}
void modify(int id,int l,int r,int x,int y,int val)
{if(x<=l&&r<=y){Add(id,l,r,val);return ;}int mid=(l+r)>>1;push_down(id,l,r);if(x<=mid) modify(id<<1,l,mid,x,y,val);if(y>mid) modify(id<<1|1,mid+1,r,x,y,val);push_up(id);return ;
}
int query(int id,int l,int r,int x,int y)
{if(x<=l&&r<=y) return tree[id];int ans=0;push_down(id,l,r);int mid=(l+r)>>1;if(x<=mid) ans+=query(id<<1,l,mid,x,y);if(y>mid) ans+=query(id<<1|1,mid+1,r,x,y);return ans;
}
signed main()
{n=read(); Q=read();for(int i=1;i<=n;++i) a[i]=read();build(1,1,n);while(Q--){scanf("%s",op);int a,b,c;if(op[0]=='C'){a=read(); b=read();c=read();modify(1,1,n,a,b,c);}else {a=read(); b=read();printf("%lld\n",query(1,1,n,a,b));}}return 0;
}
  • 区间最大值

含金量不高,但有变形 可以考虑转化,每次加一个数,即使假设全部是添加数,一共有T个,开个T长度的线段树,搞个计数器,那么每次就在那个位置单点修改,区间查询即可

具体看实现:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<iomanip>
#include<queue>
using namespace std;
const int maxn=200005;
const int inf=0x80000000;
inline int read()
{int x=0,y=1; char c=getchar();while(c<'0'||c>'9') {if(c=='-') y=-1; c=getchar();}while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();return x*y;
}
int T,mod,n,cnt(0);
int tree[maxn*4];
char op[2];
void push_up(int id)
{tree[id]=max(tree[id<<1],tree[id<<1|1]);return ;
}
void build(int id,int l,int r)
{if(l==r) {tree[id]=0; return ;}int mid=(l+r)>>1;build(id<<1,l,mid);build(id<<1|1,mid+1,r);push_up(id);return ;
}
void modify(int id,int l,int r,int pos,int val)
{if(l==r) {tree[id]=val; return ;}int mid=(l+r)>>1;if(pos<=mid) modify(id<<1,l,mid,pos,val);else modify(id<<1|1,mid+1,r,pos,val);push_up(id);return ;
}
int query(int id,int l,int r,int x,int y)
{if(x<=l&&r<=y) return tree[id];int ans=inf;int mid=(l+r)>>1;if(x<=mid) ans=max(query(id<<1,l,mid,x,y),ans);if(y>mid) ans=max(ans,query(id<<1|1,mid+1,r,x,y));return ans;
}
int pre(0);
int main()
{T=read(); mod=read();n=T; build(1,1,n);while(T--){scanf("%s",op);if(op[0]=='A'){++cnt;int val=read();modify(1,1,n,cnt,(pre+val)%mod);}else{int x=read();printf("%d\n",pre=query(1,1,n,cnt-x+1,cnt));}}return 0;
}
  •  区间开根号

bzoj3211原题。

属于线段树变形题,花仔很有趣♂ 

首先我们举个栗子,即使是1e7~1e8范围内的数,在不断开平方下取整的过程中逐渐趋近于,这时再开方仍时原数,可以进行剪枝,因此实际操作次数应该在5+次左右(1e9嘛

所以区间修改时先单点搞,复杂度不会爆,(头一次看见退化的区间修改笑死

#include<iostream>
#include<cstdio>
#include<string>
#include<cmath>
#include<string>
#include<iomanip>
#include<queue>
using namespace std;
const int maxn=100005;
typedef long long ll;
inline int read()
{int x=0,y=1; char c=getchar();while(c<'0'||c>'9') {if(c=='-') y=-1; c=getchar();}while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();return x*y;
}
int n,a[maxn],m;
struct node
{ll sum,mx;
}tree[maxn*4];
void push_up(int id)
{tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;tree[id].mx=max(tree[id<<1].mx,tree[id<<1|1].mx);return ;
}
void build(int id,int l,int r)
{if(l==r){tree[id].sum=a[l];tree[id].mx=tree[id].sum;return ;}int mid=(l+r)>>1;build(id<<1,l,mid);build(id<<1|1,mid+1,r);push_up(id);return ;
}
void modify(int id,int l,int r,int x,int y)
{if(tree[id].mx==1||tree[id].mx==0) return ;if(l==r){tree[id].sum=(ll)floor(sqrt(tree[id].mx));tree[id].mx=tree[id].sum;return ;}int mid=(l+r)>>1;if(x<=mid) modify(id<<1,l,mid,x,y);if(y>mid) modify(id<<1|1,mid+1,r,x,y);push_up(id);return ;
}
ll query(int id,int l,int r,int x,int y)
{if(x<=l&&r<=y) return tree[id].sum;int mid=(l+r)>>1;ll ans=0;if(x<=mid) ans+=query(id<<1,l,mid,x,y);if(y>mid) ans+=query(id<<1|1,mid+1,r,x,y);return ans;
}
int main()
{n=read();for(int i=1;i<=n;i++) a[i]=read();build(1,1,n);m=read();while(m--){int x,l,r;x=read(); l=read(); r=read();if(x==2) modify(1,1,n,l,r);else printf("%lld\n",query(1,1,n,l,r));}return 0;
} 
  •  区间取模和区间查询

 CF438D The Child and Sequence://www.luogu/problem/CF438D

这一题可以类比上一个区间开根号,主题思想是个优化的暴力 

先提出个结论的进行次数只有log次

证明显然,,只有p取m/2下取整时,得数最大

因此我们只要看一个区间什么时候不需要再进行取模,即区间最大值小于模数时,直接跳过

上代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<vector>
#include<cstdlib>
#include<queue>
using namespace std;
#define int long long 
const int maxn=1e5+5;
#define lc(x) x<<1
#define rc(x) x<<1|1
#define sum(x) tree[x].sum
#define mx(x) tree[x].mx
struct seg_tree
{int sum,mx;
}tree[maxn<<2];
inline int read()
{int x=0,y=1; char c=getchar();while(c<'0'||c>'9') {if(c=='-') y=-1; c=getchar();}while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); return x*y;
}
int n,m;
int val[maxn];
void push_up(int id)
{sum(id)=sum(lc(id))+sum(rc(id));mx(id)=max(mx(lc(id)),mx(rc(id)));return ;
}
void build(int id,int l,int r)
{if(l==r) {mx(id)=sum(id)=val[l]; return ;}int mid=(l+r)>>1;build(lc(id),l,mid);build(rc(id),mid+1,r);push_up(id);return ;
}
int query(int id,int l,int r,int x,int y)
{if(x<=l&&r<=y) return sum(id);int mid=(l+r)>>1;int ans=0;if(x<=mid) ans+=query(lc(id),l,mid,x,y);if(y>mid) ans+=query(rc(id),mid+1,r,x,y);return ans;
}
void modify(int id,int l,int r,int x,int y,int p)
{if(mx(id)<p) return ;if(l==r) {sum(id)%=p; mx(id)%=p; return ;}int mid=(l+r)>>1;if(x<=mid) modify(lc(id),l,mid,x,y,p);if(y>mid) modify(rc(id),mid+1,r,x,y,p);push_up(id);return ;
}
void update(int id,int l,int r,int pos,int val)
{if(l==r) {sum(id)=mx(id)=val; return ;}int mid=(l+r)>>1;if(pos<=mid) update(lc(id),l,mid,pos,val);else update(rc(id),mid+1,r,pos,val);push_up(id);return ;
}
signed main()
{n=read(); m=read();for(int i=1;i<=n;i++) val[i]=read();build(1,1,n);while(m--){int opt,l,r,x;opt=read(); l=read(); r=read();if(opt==1) printf("%lld\n",query(1,1,n,l,r));else if(opt==2){x=read(); modify(1,1,n,l,r,x);}else update(1,1,n,l,r);}return 0;
} 
  • 区间乘法&区间加法

有几点需要注意:

  1. 乘法的优先级高于加法,因此优先处理乘法的懒标
  2. 注意 乘法和加法懒标要记得每个点都初始化,乘法tag=1;加法tag=0
  3. 操作过程中,不论标记是否为0我们都要下传!尤其是乘法,可能导致直接挂一半分
  4. 局部long long即可
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<iomanip>
#include<cstdlib>
using namespace std;
typedef long long ll;
#define fabs(x) x>0?x:-x
#define register reg
inline int read()
{int x=0,y=1; char c=getchar();while(c<'0'||c>'9') {if(c=='-') y=-1; c=getchar();}while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();return x*y;
}
const int maxn=100005;
int p;
ll tree[maxn*4];
int n,m;
ll a[maxn];
struct node
{ll mul,pul;
}tag[maxn*4];
void push_up(int id)
{tree[id]=(ll)(tree[id<<1]+tree[id<<1|1])%p;return ;
}
void build(int id,int l,int r)
{tag[id].mul=1;tag[id].pul=0;if(l==r){tree[id]=(ll)a[l]%p;return ;}int mid=(l+r)>>1;build(id<<1,l,mid);build(id<<1|1,mid+1,r);push_up(id);return ;
}
inline void Add(int id,int l,int r,ll pls,ll mls)
{tag[id].mul=(ll)(tag[id].mul*mls)%p;tag[id].pul=(ll)(tag[id].pul*mls)%p;tree[id]=(ll)(tree[id]*mls)%p;tag[id].pul=(ll)(tag[id].pul+pls)%p;tree[id]=(ll)(tree[id]+(r-l+1)*pls%p)%p;return ;
}
void push_down(int id,int l,int r)
{int mid=(l+r)>>1;Add(id<<1,l,mid,tag[id].pul,tag[id].mul);Add(id<<1|1,mid+1,r,tag[id].pul,tag[id].mul);tag[id].mul=1; tag[id].pul=0;return ;
}
void modify_mult(int id,int l,int r,int x,int y,ll val)
{//cout<<"l: "<<l<<" "<<"r: "<<r<<endl;if(x<=l&&r<=y) {Add(id,l,r,0,val); return ;}push_down(id,l,r);int mid=(l+r)>>1;if(x<=mid) modify_mult(id<<1,l,mid,x,y,val);if(y>mid) modify_mult(id<<1|1,mid+1,r,x,y,val);push_up(id);return ;
} 
void modify_add(int id,int l,int r,int x,int y,ll val)
{if(x<=l&&r<=y) {Add(id,l,r,val,1); return ;}push_down(id,l,r);int mid=(l+r)>>1;if(x<=mid) modify_add(id<<1,l,mid,x,y,val);if(y>mid) modify_add(id<<1|1,mid+1,r,x,y,val);push_up(id);return ;
}
ll query(int id,int l,int r,int x,int y)
{if(x<=l&&r<=y) return tree[id]%p;int mid=(l+r)>>1; ll ans(0);push_down(id,l,r);if(x<=mid) ans+=query(id<<1,l,mid,x,y)%p;if(y>mid) ans+=query(id<<1|1,mid+1,r,x,y)%p;return ans%p;
}
int main()
{n=read(); p=read();for(int i=1;i<=n;i++) scanf("%lld",&a[i]);build(1,1,n);m=read();while(m--){int opt=read();if(opt==1){int l,r; ll val;l=read(); r=read(); scanf("%lld",&val);modify_mult(1,1,n,l,r,val%p);}else if(opt==2){int l,r; ll val;l=read(); r=read(); scanf("%lld",&val);modify_add(1,1,n,l,r,val%p);}else{int l,r; l=read(); r=read();printf("%lld\n",query(1,1,n,l,r));}}return 0;
}
  •  区间合并类线段树

 最好的例子就是这道题

简要题意就是求区间最大子段和,外加单点修改

重点就在如何维护区间上传的信息维护,这里需要四个参数,区间所有元素和,区间最大子段和,以左端元素为左端点的最大区间和,以右端元素为右端点的最大区间和

合并时考虑最大子段和是否超过区间位置,因此分两种大情况考虑

 注意区间合并时的情况讨论

注意这里:坑点;答案如果覆盖区间的左右段,需要再合并一次,合并操作同pushup操作

#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<string>
using namespace std;
const int maxn=500005;
const int min_inf=0x80000000;
#define int long long
inline int read()
{int x=0,y=1; char c=getchar();while(c<'0'||c>'9') {if(c=='-') y=-1; c=getchar();}while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();return x*y;
}
int n,a[maxn],m;
struct node
{int sum,dat; //dat最大子段和int lmax,rmax; 
}tree[maxn*4];
void push_up(int id)
{int lc=id<<1,rc=id<<1|1;tree[id].sum=tree[lc].sum+tree[rc].sum;tree[id].lmax=max(tree[lc].lmax,tree[lc].sum+tree[rc].lmax);tree[id].rmax=max(tree[rc].rmax,tree[rc].sum+tree[lc].rmax);tree[id].dat=max(tree[lc].dat,tree[rc].dat);tree[id].dat=max(tree[id].dat,tree[lc].rmax+tree[rc].lmax);return ;
}
void build(int id,int l,int r)
{if(l==r){tree[id].sum=a[l];tree[id].dat=tree[id].sum;tree[id].rmax=tree[id].lmax=tree[id].sum;return ;}int mid=(l+r)>>1;build(id<<1,l,mid);build(id<<1|1,mid+1,r);push_up(id);return ;
}
void modify(int id,int l,int r,int pos,int x)
{if(l==r){tree[id].sum=x;tree[id].dat=tree[id].rmax=tree[id].lmax=x;return ;}int mid=(l+r)>>1;if(pos<=mid) modify(id<<1,l,mid,pos,x);else modify(id<<1|1,mid+1,r,pos,x);push_up(id);return ;
}
node query(int id,int l,int r,int x,int y)
{if(x<=l&&r<=y) return tree[id];int mid=(l+r)>>1;node ans,ta,tb;if(y<=mid) return query(id<<1,l,mid,x,y);if(x>mid) return query(id<<1|1,mid+1,r,x,y);ta=query(id<<1,l,mid,x,y); tb=query(id<<1|1,mid+1,r,x,y);ans.sum=ta.sum+tb.sum;ans.lmax=max(ta.lmax,ta.sum+tb.lmax);ans.rmax=max(tb.rmax,tb.sum+ta.rmax);ans.dat=max(max(ta.dat,tb.dat),ta.rmax+tb.lmax);return ans;
}
signed main()
{n=read(); m=read();for(int i=1;i<=n;i++) a[i]=read();build(1,1,n);while(m--){int opt,x,y;opt=read(); x=read(); y=read();if(opt==2) modify(1,1,n,x,y);else {if(x>y) swap(x,y); printf("%lld\n",query(1,1,n,x,y).dat);}}return 0;
}
  • 区间gcd和区间修改

一道例题:interval GCD

膜你赛曾经打过原题,还写过题解 自卖自夸

就两个要点:

  1. 根据《九章算术》之更相减损术 二死了,,它扩展到多个数同时相减,这就构成了差分
  2. 因此构造差分序列,维护区间加只需要线段树两个单点修改,维护区间的左端点只需要用树状数组维护原差分序列,就van♂事了

代码没啥,相信各位都能手撕+吊打: 

#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
#include<string>
#include<map>
#include<iomanip> 
using namespace std;
const int maxn=500005;
#define fabs(x) x>0?x:-x
//typedef long long ll;
#define int long long
inline int read()
{int x=0,y=1; char c=getchar();while(c<'0'||c>'9') {if(c=='-') y=-1; c=getchar();}while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();return x*y;
}
int n,a[maxn];
int m;
int gcd(int a,int b)
{if(!b) return a;return gcd(b,a%b);
}
struct BITtree
{int c[maxn];int lowbit(int x) {return x&(-x);}void add(int now,int x){for(;now<=n;now+=lowbit(now)) c[now]+=x;return ;}int query(int now){int sum=0;for(;now;now-=lowbit(now)) sum+=c[now];return sum;}
}bit;
int tree[maxn*4];
void pushup(int id)
{tree[id]=gcd(tree[id<<1],tree[id<<1|1]);return ;
}
void build(int id,int l,int r)
{if(l==r){tree[id]=a[l]-a[l-1];return ;}int mid=(l+r)>>1;build(id<<1,l,mid);build(id<<1|1,mid+1,r);pushup(id);return ; 
}
void modify(int id,int l,int r,int pos,int val)
{if(l==r){tree[id]+=val;return ;}int mid=(l+r)>>1;if(pos<=mid) modify(id<<1,l,mid,pos,val);else m

更多推荐

线段树合集整理

本文发布于:2024-03-23 20:32:33,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1742521.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:线段   合集

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!