首页 > 其他分享 >线段树

线段树

时间:2023-09-07 19:45:53浏览次数:21  
标签:线段 mid long 查询 修改 tag

树状数组是个好东西,写起来也相对好看。但是操作比较局限,区间修改就掉回\(O(nlogn)\), 那还不如 \(O(n)\)。线段树完美的解决问题。

线段树,也可以理解的一堆线段组成的树。

大致如上,有一线段 \([l, r]\) 当 \(l \ne r\) 可化为 \([l, mid]\) , \([mid + 1, r]\)

PS: \(mid = (l + r) / 2\)

增加

此操作 不是 新加节点,是将一段区间增加一个数。

将 [2, 9] 每个数增加 1

像这样通过递归的方式修改这些节点,然后我们惊喜的发现,访问了 \(n logn\) 个节点......似乎不如没有

这个时候lazy_tag登场(懒人tag)

蓝色的就是lazy_tag(下文称为tag),标记增加了多少, 这时 \([2,2], [3,3],[4,5], [6,8], [9,9]\) 都有 tag = 1。

用处放到下文

查询

这里,我们可以查询区间的和

在上例子里我们查询 [1, 1]时,不会有问题,查询 [1, 2]时,不会有问题, 查询 [1, 10]时,不会有问题, 但是查询 [6, ]7时,有问题, 因为没有修改到。

因为 [6, 8] 被标有tag, 然而,在访问 [6, 7]时一定会访问到6, 8,那么我们便可以将tag传下来。

也就是不访问就不修改。

运用以上方案,就可以实现单点修改,单点查询,区间修改(同增同减), 区间查询。

#include<iostream>

using namespace std;

const long long kMaxN = 1e5 + 5; 

long long n, m, a[kMaxN << 2]; //注意开大4倍

struct L {
	long long sum, tag;
} sgt[kMaxN << 2];

void push_up(long long rt) { //更新这个节点
	sgt[rt].sum = sgt[rt << 1].sum + sgt[rt << 1 | 1].sum;
}	


void build_tree(long long rt, long long l, long long r) { //建树
	sgt[rt].tag=0;
	if (l == r) {
		sgt[rt].sum = a[l];
		return;
	}
	long long mid = l + r >> 1; 
	build_tree(rt << 1, l, mid), build_tree(rt << 1 | 1, mid + 1, r);
	push_up(rt);
}

void add_tag(long long rt, long long l, long long r,long long d) {//建立tag
	sgt[rt].tag += d;
	sgt[rt].sum += d * (r - l + 1);//值也增加
}

void push_down(long long rt, long long l, long long r) { //tag往下传
	if (sgt[rt].tag) {
		long long mid = l + r >> 1;
		add_tag(rt << 1, l, mid, sgt[rt].tag);
		add_tag(rt << 1 | 1, mid + 1, r, sgt[rt].tag);
		sgt[rt].tag = 0;
	}
}

void update(long long L, long long R, long long rt, long long l, long long r, long long d){ //更新[l, r]的值 + d
	if (L <= l && r <= R) {
		add_tag(rt, l, r, d);
		return;
	}
	push_down(rt, l, r);
	long long mid = l + r >> 1;
	if (L <= mid) {
		update(L, R, rt << 1, l, mid, d);
	} if (mid < R) {
		update(L, R, rt << 1 | 1, mid + 1, r, d);
	}
	push_up(rt);
} 

long long query(long long L, long long R, long long rt, long long l, long long r) {//找到 [L, R] 的和
	if (L <= l && r <= R) {
		return sgt[rt].sum;
	}
	push_down(rt, l, r);
	long long mid = l + r >> 1, ans = 0;
	if (L <= mid) {
		ans += query(L, R, rt << 1, l, mid);
	} 
	if (mid < R) {
		ans += query(L, R, rt << 1 | 1, mid + 1, r);
	}
	return ans;
}

int main(){
	cin >> n >> m;
	for (long long i = 1; i <= n; i++) {
		cin >> a[i];
	}
	build_tree(1, 1, n);
	for(long long i = 1, a, x, y, d; i <= m; i++) {
		cin >> a >> x >> y;
		if (a == 1) {
			cin >> d;
			update(x, y, 1, 1, n, d);
		} else {
			cout << query(x, y, 1, 1, n) << '\n';
		}
	}
	return 0;
} 

标签:线段,mid,long,查询,修改,tag
From: https://www.cnblogs.com/jiangyuchen12/p/17685901.html

相关文章

  • 线段树进阶
    普通线段树核心在于上传标记(pushup)和下传标记(pushdown)以及懒标记的设计。P3373【模板】线段树2维护一个加法标记和乘法标记。下传标记时,将乘法标记更新加法标记。标记下传实现voidpushdown(intu,intl,intr){intmid=l+r>>1;tr[u<<1].val=(tr[u<<1].val*tr[......
  • Codeforces Round 406 (Div. 2) D. Legacy 线段树优化建图
    传送门题目大意:给定n个点,m个操作,和起点s。其中n和q大于等于1小于等于1e5,s大于等于1小于等于n其中m个操作有三种情况:  1.输入1uvval表示从u号点向v号点连一个权值为val的有向边,其中1<=u<=n,1<=v<=n,1<=val<=1e9  2.输入2ulrval表示从u号点......
  • 吉司机线段树
    一、区间历史最值以区间历史最大值为例。首先,相应地,设\(maxb\)表示一个节点的区间历史最大值。为了更新一个区间的子区间,再设一个\(tag2\),表示\(tag1\)从上次\(push\_down\)以后到现在达到过的最大值。\(code:\)voidpush_up(intu){p[u].w=p[u<<1].w+p[u<<1|1].w......
  • 李超线段树学习笔记
    李超线段树学习笔记P4097【模板】李超线段树/[HEOI2013]Segment题意要求在平面直角坐标系下维护两个操作:在平面上加入一条线段。记第\(i\)条被插入的线段的标号为\(i\)。给定一个数\(k\),询问与直线\(x=k\)相交的线段中,交点纵坐标最大的线段的编号。做法首先,......
  • 线段树
    建树:inta[100005],d[100005];voidbuild(ints,inte,intp){//建树 //对区间[s,t]建立线段树,当前根编号为p if(s==e){ d[p]=a[s]; return; } intm=s+((e-s)>>1); build(s,m,p*2); build(m+1,e,p*2+1);//分割出两个子区间 d[p]=d[p*2]+d[(p*2)+1];}//d[i]为......
  • P3373 【模板】线段树 2
    【模板】线段树2如题,已知一个数列,你需要进行下面三种操作:将某区间每一个数乘上\(x\);将某区间每一个数加上\(x\);求出某区间每一个数的和。输入格式第一行包含三个整数\(n,q,m\),分别表示该数列数字的个数、操作的总个数和模数。第二行包含\(n\)个用空格分隔的整数,其......
  • Daimayuan Online Judge 线段树打标记2
    给\(n\)个数\(a_1,a_2,\cdots,a_n\)。支持\(q\)个操作:1lrd,令所有的\(a_i(l\leqi\leqr)\)加上\(d\)。2lrd,令所有的\(a_i(l\leqi\leqr)\)乘上\(d\)。3lrd,令所有的\(a_i(l\leqi\leqr)\)等于\(d\)。4lr,查询\((\sum_{i=l......
  • Daimayuan Online Judge 线段树打标记1
    给\(n\)个数\(a_1,a_2,\cdots,a_n\)。支持\(q\)个操作:1lrd,令所有的\(a_i(l\leqi\leqr)\)加上\(d\)。2lr,查询\(max_{i=l}^{r}a_i\)。区间修改的线段树要比基础线段树多考虑一个元素:\(lazy\tag\)。复杂的信息可以用多个标记表示。\(lazy\ta......
  • Daimayuan Online Judge 线段树2
    给\(n\)个数\(a_1,a_2,\cdots,a_n\)。支持\(q\)个操作:1xd,修改\(a_x=d\)。2lr,查询\([l,r]\)中的最大子段和。一:确定需要维护的信息。根据分治中线讨论,哪些信息可以合并出所需信息。递归讨论新信息如何合并。直至完全拆解。不越过分治中线:\([l,r]\)......
  • Daimayuan Online Judge 线段树1
    给\(n\)个数\(a_1,a_2,\cdots,a_n\)。支持\(q\)个操作:1.1xd,修改\(a_x=d\)。2.2lr,查询\(min_{i=l}^{r}a_i\),并输出\(\sum_{i=l}^{r}[a_i=min_{i=l}^{r}a_i]\)。一:确定出需要维护的信息\(Info\)。建立线段树节点structInfo{ intminv......