[模板]线段树

如题,给出一种数据结构的模板

#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>

using namespace std;

class SegmentTree {
	private:
		int fp;
			
		struct Tnode {
			int LeftChild,RightChild;
			int l,r;
			int Max,Min;
			int delta;
			long long sum;
			
			Tnode() {
				LeftChild = RightChild = l = r = Max = Min = delta = 0;
				sum = 0ll;
			}
		}tree[100010];
		
		void pushdown(int root) {
			if(tree[root].delta == 0) return;
			Tnode &lc = tree[ tree[root].LeftChild ],&rc = tree[ tree[root].RightChild ];
			int lenlc = getlen(tree[root].LeftChild),lenrc = getlen(tree[root].RightChild);
			
			lc.delta += tree[root].delta;
			rc.delta += tree[root].delta;
			
			lc.Max += tree[root].delta;
			rc.Max += tree[root].delta;
			
			lc.Min += tree[root].delta;
			rc.Min += tree[root].delta;
			
			lc.sum += tree[root].delta * lenlc;
			rc.sum += tree[root].delta * lenrc;
			
			tree[root].delta = 0;
		}
		
		void pushup(int root) {
			Tnode &lc = tree[ tree[root].LeftChild ],&rc = tree[ tree[root].RightChild ];
			
			tree[root].sum = lc.sum + rc.sum;
			tree[root].Max = max(lc.Max,rc.Max);
			tree[root].Min = min(lc.Min,rc.Min);
		}
		
		int getpoint(int l,int r) {
			++fp;
			
			tree[fp].l = l;
			tree[fp].r = r;
			
			return fp;
		}
		
		int getlen(int root) {
			return tree[root].r - tree[root].l + 1;
		}
		
	public:
		SegmentTree() {
			fp = 0;
		}
		
		int create(int l,int r) {
			int root = getpoint(l,r);
			
			if(l == r) {
				return root;
			}
			
			tree[root].LeftChild = create(l,l + r >> 1);
			tree[root].RightChild = create((l + r >> 1) + 1,r);
			
			return root;
		}
		
		long long query_sum(int root,int l,int r) {
			if(tree[root].r < l || r < tree[root].l) {
				return 0ll;
			}
			
			if(l <= tree[root].l && tree[root].r <= r) {
				return tree[root].sum;
			}
			
			long long result = 0;
			result += query_sum(tree[root].LeftChild,l,r);
			result += query_sum(tree[root].RightChild,l,r);
			
			return result;
 		}
		
		void update(int root,int l,int r,int delta) {
			if(tree[root].r < l || r < tree[root].l) {
				return;
			}
			
			if(l <= tree[root].l && tree[root].r <= r) {
				tree[root].delta += delta;
				tree[root].Max += delta;
				tree[root].Min += delta;
				tree[root].sum += getlen(root) * delta;
				
				return;
			}
			pushdown(root);
			
			update(tree[root].LeftChild,l,r,delta);
			update(tree[root].RightChild,l,r,delta);
			
			pushup(root);
		}
		
		int query_max(int root,int l,int r) {
			if(tree[root].r < l || r < tree[root].l) {
				return 0;
			}
			
			if(l <= tree[root].l && tree[root].r <= r) {
				return tree[root].Max;
			}
			
			int result = 0;
			result = max(result,query_max(tree[root].LeftChild,l,r));
			result = max(result,query_max(tree[root].RightChild,l,r));
			
			return result;
		}
		
		int query_min(int root,int l,int r) {
			if(tree[root].r < l || r < tree[root].l) {
				return 0;
			}
			
			if(l <= tree[root].l && tree[root].r <= r) {
				return tree[root].Min;
			}
			
			int result = 0;
			result = min(result,query_min(tree[root].LeftChild,l,r));
			result = min(result,query_min(tree[root].RightChild,l,r));
			
			return result;
		}
};

SegmentTree st;

int main() {
	int n;
	cin >> n;
	int root = st.create(1,n);
	
	int m;
	cin >> m;
	for(int i = 0;i < m;i++) {
		string oper;
		cin >> oper;
		
		int l,r;
		cin >> l >> r;
		if(oper == "Add") {
			int delta;
			cin >> delta;
			st.update(root,l,r,delta);
		}
		if(oper == "Sum") {
			cout << st.query_sum(root,l,r) << endl;
		}
		if(oper == "Max") {
			cout << st.query_max(root,l,r) << endl;
		}
		if(oper == "Min") {
			cout << st.query_min(root,l,r) << endl;
		}
	}
	return 0;
}


支持求和,求最大值,最小值,区间修改的操作

简单描述:

将线段树写成一个类,设置会访问到的函数为公开,比如说查询,修改的操作

将不会使用到,但在类中互相调用的设置为私有

简单描述原理:

一开始建树,把这一棵树的每一个节点都给予一个保存的线段的左右端点

通过lazy-tag的方式,保存每一次需要增加的值,如果这一个节点中所有的子节点都会被这个操作影响,就把标记放置在当前位置

如果需要修改它的子节点,就会把标记下传

在求区间的值的时候,判断是否包含或者相交,分解问题,求出每一个子问题,最后得到答案

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值