单点修改,区间查询
区间修改,区间查询(利用极长线段覆盖的思想)
复杂度为O(logn)
建树的复杂度为o(n),因为节点数有2n-1个。
#include <bits/stdc++.h> //线段树->进行递归 using namespace std; #define N 500050 /*#define N 500050 int a[N]; struct segmentTree{ struct Node{ int l,r; long long sum;//做的是区间求和 有2n-1个节点 开4n个节点即可 }node[N<<2]; }seg; void build(int i,int l,int r){ seg.node[i].l=l,seg.node[i].r=r; if(l==r){ seg.node[i].sum=a[l]; return; } int mid=(l+r)/2; build(i*2,l,mid); build(i*2+1,mid+1,r); seg.node[i].sum=seg.node[i*2].sum+seg.node[i*2+1].sum; } */ struct SegmentTree2{ #define ls (i<<1) #define rs (ls|1) #define mid (l+r>>1) long long sum[N<<2],tag[N<<2]; void build(int i,int l, int r){ tag[i]=0;//置零,初始化 if(l==r){ scanf("%d",&sum[i]); } else{ build(ls,l,mid),build(rs,mid+1,r); sum[i]=sum[ls]+sum[rs]; } } void add(int i,int l,int r,int x,int k){ sum[i]+=k; if(l==r) return; if(x<=mid) add(ls,l,mid,x,k); else add(rs,mid+1,r,x,k); } //动态开点就再封装一层 void pushdown(int i,int l,int r){ if(tag[i]==0) return; sum[ls]+=tag[i]*(mid-l+1),sum[rs]+=tag[i]*(r-mid); tag[ls]+=tag[i],tag[rs]+=tag[i]; tag[i]=0; } void add1(int i,int l,int r,int L,int R,long long k){ //o(n) 递归到每一片叶子) if(l>R||r<L) return; /*if(l==r){ sum[i]+=k; return; } add1(ls,l,mid,L,R,k),add1(rs,mid+1,r,L,R,k); sum[i]=sum[ls]+sum[rs];*/ else if(L<=l&&r<=R){ sum[i]+=k*(r-l+1),tag[i]+=k; } else{ pushdown(i,l,r); add1(ls,l,mid,L,R,k); add1(rs,mid+1,r,L,R,k); sum[i]=sum[ls]+sum[rs];//可以封装成update } } long long query(int i,int l,int r,int L,int R){ if(L<=l&&r<=R) return sum[i]; else if(l>R||r<L) return 0; //else return query(ls,l,mid,L,R)+query(rs,mid+1,r,L,R); else{ pushdown(i,l,r); return query(ls,l,mid,L,R)+query(rs,mid+1,r,L,R); } } }seg1; // 单点修改 a[x]+k 只影响一条链上的含x的链 o(logn) //查询 [l,r]用极长的线段去覆盖,每层4个,logn层,o(logn) int n,m; int main(){ scanf("%d%d",&n,&m); seg1.build(1,1,n); while(m--){ int op,a,b; scanf("%d %d %d",&op,&a,&b); if(op==1) seg1.add(1,1,n,a,b); else printf("%lld\n",seg1.query(1,1,n,a,b)); } return 0; } //懒标记 对于一次区间修改,极长线段打上懒标记 //为了保证子节点的信息正确,把懒标记下推给儿子节点,清空当前节点的标记 //另一个策略,永久化标记 //区间修改,区间查询
最最基本的板子了,视频参见2022寒假培训 线段树(一)_哔哩哔哩_bilibili
从这里慢慢补起吧,陌生人~
#include <bits/stdc++.h> using namespace std; #define N 100000 struct segmentTree{ #define lr (i<<1) #define rr (lr|1) #define mid (lr+rr>>1) long long sum[N<<2],tag[N<<2]; void build(int i,int l,int r){ tag[i]=0;//置零 if(l==r) scanf("%d",&sum[i]); else{ build(lr,l,mid); build(rr,mid+1,r); sum[i]=sum[lr]+sum[rr]; } } void add(int i,int l,int r,int x,int k){ sum[i]+=k; if(l==r) return; if(x<=mid) add(lr,l,mid,x,k); else{ add(rr,mid+1,r,x,k); } } void pushdown(int i,int l,int r){ if(tag[i]==0) return; sum[lr]+=tag[i]*(mid-l+1),sum[rr]+=tag[i]*(r-mid); tag[lr]+=tag[i],tag[rr]+=tag[i]; tag[i]=0;//取消标记 } void add1(int i,int l,int r,int L,int R,int k){ if(l>R||r<L) return; else if(L<=l&&r<=R){ sum[i]+=k*(r-l+1),tag[i]+=k; } else{ pushdown(1,l,r); add1(lr,l,mid,L,R,k); add1(rr,mid+1,r,L,R,k); sum[i]=sum[rr]+sum[lr]; } } long long query(int i,int l,int r,int L,int R){ if(L<=l&&r<=R) return sum[i]; else if(l>R||r<L) return 0; else return query(lr,l,mid,L,R)+query(rr,mid+1,r,L,R); } long long query1(int i,int l,int r,int L,int R){ if(L<=l&&r<=R) return sum[i]; else if(l>R||r<L) return 0; else{ pushdown(i,l,r); return query(lr,l,mid,L,R)+query(rr,mid+1,r,L,R); } } }seg; int n,m; int main(){ scanf("%d%d",&n,&m); seg.build(1,1,n); while(m--){ int op,a,b; scanf("%d %d %d",&op,&a,&b); if(op==1) seg.add(1,1,n,a,b); else printf("%lld\n",seg.query(1,1,n,a,b)); } return 0; }
用到单点查询的情况不多,但是一般用到的话,封装左右端点比较容易。上面的板子是用附加传参代替了左右端点。
#include<iostream>
#include<cstdio>
#define ll long long
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid ((l+r)>>1)
#define update(rt) t[rt].data=t[ls].data+t[rs].data
#define N 500005
//宏定义大法好
using namespace std;
struct tree{
ll tag,data;
int left,right;
}t[(N<<4)+5];//之前因为数组开小了,RE了3个点
int n,m,pos;
ll s[N],ans;
int a,b,x,k;
inline void build(int rt,int l,int r){
//递归建树
t[rt].left=l;t[rt].right=r;t[rt].tag=0;
if(l==r){
t[rt].data=s[l];
return ;
}
build(ls,l,mid);
build(rs,mid+1,r);
update(rt);
return ;
}
inline void pushdown(int rt){//标记下传
t[ls].tag+=t[rt].tag;
t[rs].tag+=t[rt].tag;
t[ls].data+=t[rt].tag*(t[ls].right-t[ls].left+1);
t[rs].data+=t[rt].tag*(t[rs].right-t[rs].left+1);
t[rt].tag=0;
return ;
}
inline void addval(int rt){
//区间修改
int l=t[rt].left,r=t[rt].right;
if(a<=l&&r<=b){
t[rt].tag+=x;
t[rt].data+=(t[rt].right-t[rt].left+1)*x;
return ;
}
if(t[rt].tag) pushdown(rt);
if(a<=mid) addval(ls);
if(b>mid) addval(rs);
update(rt);
return ;
}
inline void query(int rt){
//单点查询
a=pos,b=pos;
int l=t[rt].left,r=t[rt].right;
if(a<=l&&r<=b){
ans+=t[rt].data;
return ;
}
if(t[rt].tag) pushdown(rt);
if(a<=mid) query(ls);
if(b>mid) query(rs);
return ;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%lld",&s[i]);
build(1,1,n);
for(int i=1;i<=m;++i){
scanf("%d",&k);
if(k==1){
scanf("%d%d%d",&a,&b,&x);
addval(1);//区间修改
}
else{
scanf("%d",&pos);//要查询的位置
query(1);
printf("%lld\n",ans);
ans=0;
}
}
return 0;
}
洛谷的线段树二
#include <bits/stdc++.h>
using namespace std;
#define N 100010
int n,m,p;
// a[j]*mul[ls]+add[ls]
//( a[j]*mul[ls]+add[ls])*mul[i]+add[i] a[j]*mul[ls]*mul[i]+add[ls]*mul[i]+add[i];
struct segmentTree{
#define ls (i<<1)
#define rs (ls|1)
#define mid (l+r>>1)
int sum[N<<2],add[N<<2],mul[N<<2];
void build(int i,int l,int r){
add[i]=0,mul[i]=1;
if(l==r) scanf("%d",&sum[i]),sum[i]%=p;
else{
build(ls,l,mid),build(rs,mid+1,r);
sum[i]=(sum[ls]+sum[rs])%p;
}
}
void pushdown(int i,int l,int r){
//分三步
mul[ls]=1ll*mul[ls]*mul[i]%p;
mul[rs]=1ll*mul[rs]*mul[i]%p;
add[ls]=(1ll*add[ls]*mul[i]%p+add[i])%p;
add[rs]=(1ll*add[rs]*mul[i]%p+add[i])%p;
sum[ls]=(1ll*sum[ls]*mul[i]%p+1ll*(mid-l+1)*add[i]%p)%p;
sum[rs]=(1ll*sum[rs]*mul[i]%p+1ll*(r-mid)*add[i]%p)%p;
add[i]=0,mul[i]=1;
}
void Mul(int i,int l,int r,int L,int R,int k){
if(l>R||r<L) return;
else if(L<=l&&r<=R){
sum[i]=1ll*sum[i]*k%p;
mul[i]=1ll*mul[i]*k%p;
add[i]=1ll*add[i]*k%p;
}
else{
pushdown(i,l,r);
Mul(ls,l,mid,L,R,k);
Mul(rs,mid+1,r,L,R,k);
sum[i]=(sum[ls]+sum[rs])%p;
}
}
void Add(int i,int l,int r,int L,int R,int k){
if(l>R||r<L) return;
else if(L<=l&&r<=R){
sum[i]=(sum[i]+1ll*(r-l+1)*k%p)%p;
add[i]=(add[i]+k)%p;
}
else{
pushdown(i,l,r);
Add(ls,l,mid,L,R,k);
Add(rs,mid+1,r,L,R,k);
sum[i]=(sum[ls]+sum[rs])%p;
}
}
long long query(int i,int l,int r,int L,int R){
if(L<=l&&r<=R) return sum[i];
else if(l>R||r<L) return 0;
else{
pushdown(i,l,r);
return (query(ls,l,mid,L,R)+query(rs,mid+1,r,L,R))%p;
}
}
}seg;
int main(){
scanf("%d%d%d",&n,&m,&p);
seg.build(1,1,n);
while(m--){
int op,a,b,c;
scanf("%d%d%d",&op,&a,&b);
if(op!=3) scanf("%d",&c);
if(op==1) seg.Mul(1,1,n,a,b,c);
else if(op==2) seg.Add(1,1,n,a,b,c);
else printf("%d\n",seg.query(1,1,n,a,b));
}
return 0;
}