基本概念
线段树是一种二叉搜索树,什么叫做二叉搜索树,首先满足二叉树,每个结点度小于等于二,即每个结点最多有两颗子树,何为搜索,我们要知道,线段树的每个结点都存储了一个区间,也可以理解成一个线段,而搜索,就是在这些线段上进行搜索操作得到你想要的答案。可以在线维护修改以及查询区间上的最值,求和。
例:对于A[1:6] = {1,8,6,4,3,5}来说,线段树如上所示,红色代表每个结点存储的区间,蓝色代表该区间最值:
线段树是算法竞赛中常用的用来维护 区间信息 的数据结构。
线段树可以在 的时间复杂度内实现单点修改、区间修改、区间查询(区间求和,求区间最大值,求区间最小值)等操作。
模板
不想写内容。
例题
#include<iostream>
typedef long long LL;
LL n,a[100005],d[270000],b[270000];
void build(LL l,LL r,LL p){
if(l == r){
d[p] = a[l];
return ;
}
LL m = (l+r)>>1;
build(l,m,p<<1);build(m+1,r,(p<<1)|1);
d[p] = d[p<<1] + d[(p<<1)|1];
}
void update(LL l,LL r,LL c,LL nl,LL nr,LL p){
if(l <= nl && r >=nr){
d[p] += (nr - nl + 1)*c;
b[p] += c;
return ;
}
LL mid = nl + ((nr - nl) >> 1);
if(b[p]){
d[p<<1] += b[p] *(mid - nl + 1);
d[(p<<1)|1] += b[p]*(nr - mid);
b[p << 1] += b[p];
b[(p << 1)|1] += b[p];
}
b[p] = 0;
if(l <= mid) update(l,r,c,nl,mid,p << 1);
if(r > mid) update(l,r,c,mid+1,nr,(p << 1)|1);
d[p] = d[p << 1] + d[(p << 1)|1];
}
LL getsum(LL l,LL r,LL nl,LL nr,LL p){
if(l <= nl && nr <= r) return d[p];
LL mid = nl + ((nr - nl) >> 1);
if(b[p]){
d[p<<1] += b[p] *(mid - nl+1);
d[(p << 1)|1] += b[p]*(nr - mid);
b[p << 1] += b[p];
b[(p << 1)|1] += b[p];
}
b[p] = 0;
LL sum = 0;
if(l <= mid) sum = getsum(l,r,nl,mid,p << 1);
if(r > mid) sum += getsum(l,r,mid+1,nr,(p << 1)|1);
d[p] = d[p << 1] + d[(p << 1) | 1];
return sum;
}
int main() {
std::ios::sync_with_stdio(0);
LL q,i1,i2,i3,i4;
std::cin>>n>>q;
for(int i = 1;i<=n;i++){
std::cin>>a[i];
}
build(1,n,1);
while(q--){
std::cin>>i1>>i2>>i3;
if(i1 == 2){
std::cout<<getsum(i2,i3,1,n,1)<<std::endl;
}
else{
std::cin>>i4;
update(i2,i3,i4,1,n,1);
}
}
return 0;
}
#include<iostream>
typedef long long LL;
LL a[100005],d[270000],sum[270000],mul[270000],mod;
int n,m;
LL read() {
LL w = 1, q = 0;
char ch = ' ';
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') w = -1, ch = getchar();
while (ch >= '0' && ch <= '9') q = (LL)q * 10 + ch - '0', ch = getchar();
return (LL)w * q;
}
void up(int p){
d[p] = d[p<<1] + d[(p<<1)|1];
d[p] %= mod;
}
void lazy(int p,int nl,int nr){
int mid = (nr + nl) >> 1;
int l = p << 1;
int r = (p << 1)|1;
if(mul[p] != 1){
mul[l] *= mul[p];
sum[l] *= mul[p];
d[l] *= mul[p];
mul[l] %= mod;
sum[l] %= mod;
d[l] %= mod;
mul[r] *= mul[p];
sum[r] *= mul[p];
d[r] *= mul[p];
mul[r] %= mod;
sum[r] %= mod;
d[r] %= mod;
mul[p] = 1;
}
if(sum[p]){
sum[l] += sum[p];
sum[l] %= mod;
d[l] += (mid - nl + 1)* sum[p];
d[l] %= mod;
sum[r] += sum[p];
sum[r] %= mod;
d[r] += (nr - mid)*sum[p];
d[r] %= mod;
sum[p] = 0;
}
}
void build(int l,int r,int p){
mul[p] = 1;
if(l == r){
d[p] = a[l];
return ;
}
int mid = l + ((r - l) >> 1);
build(l,mid,p<<1);build(mid+1,r,(p<<1)|1);
up(p);
}
void multi(int l, int r, LL z, int s, int t, int i) {
int mid = s + ((t - s) >> 1);
if (l <= s && t <= r) {
mul[i] *= z;
mul[i] %= mod; // 这是取模的
sum[i] *= z;
sum[i] %= mod; // 这是取模的
d[i] *= z;
d[i] %= mod; // 这是取模的
return;
}
lazy(i, s, t);
if (mid >= l) multi(l, r, z, s, mid, (i << 1));
if (mid + 1 <= r) multi(l, r, z, mid + 1, t, (i << 1) | 1);
up(i);
}
void add(int l, int r, LL z, int s, int t, int i) {
int mid = s + ((t - s) >> 1);
if (l <= s && t <= r) {
d[i] += z * (t - s + 1);
d[i] %= mod; // 这是取模的
sum[i] += z;
sum[i] %= mod; // 这是取模的
return;
}
lazy(i, s, t);
if (mid >= l) add(l, r, z, s, mid, (i << 1));
if (mid + 1 <= r) add(l, r,z, mid + 1, t, (i << 1) | 1);
up(i);
}
LL getsum(int l,int r,int nl,int nr,int p){
if(l <= nl && r >= nr) return d[p];
lazy(p,nl,nr);
int mid = nl+((nr - nl)>>1);
LL answer = 0;
if(mid >=l) answer += getsum(l,r,nl,mid,p<<1);
if(mid < r) answer += getsum(l,r,mid+1,nr,(p<<1)|1);
return answer%mod;
}
int main() {
int i1,i2,i3;
LL i4;
n = read();
m = read();
mod = read();
for(int i = 1;i<=n;i++) a[i] = read();
build(1, n, 1); // 建树
while(m--){
i1 = read();
if(i1 == 3){
i2 = read();
i3 = read();
std::cout<<getsum(i2,i3,1,n,1)<<std::endl;
}
else if(i1 == 2){
i2 = read();
i3 = read();
i4 = read();
add(i2,i3,i4,1,n,1);
}
else if(i1 == 1){
i2 = read();
i3 = read();
i4 = read();;
multi(i2,i3,i4,1,n,1);
}
}
return 0;
}
参考:
[1] 线段树详解,Xenny