7.10 线段树

107 阅读2分钟

基本概念

线段树是一种二叉搜索树,什么叫做二叉搜索树,首先满足二叉树,每个结点度小于等于二,即每个结点最多有两颗子树,何为搜索,我们要知道,线段树的每个结点都存储了一个区间,也可以理解成一个线段,而搜索,就是在这些线段上进行搜索操作得到你想要的答案。可以在线维护修改以及查询区间上的最值,求和。

例:对于A[1:6] = {1,8,6,4,3,5}来说,线段树如上所示,红色代表每个结点存储的区间,蓝色代表该区间最值:

image.png

线段树是算法竞赛中常用的用来维护 区间信息 的数据结构。

线段树可以在 O(logN)O(\log N) 的时间复杂度内实现单点修改、区间修改、区间查询(区间求和,求区间最大值,求区间最小值)等操作。

模板

oi-wiki.org/ds/seg/#%E6…

不想写内容。

例题

P3372 【模板】线段树 1

#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;
}

P3373 【模板】线段树 2

#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