线段树(区间求和,区间加,单点更新等)

线段树

线段树是一种二叉搜索树。它将一段区间划分为若干个单位区间,每一个节点都储存着一个区间。

线段树可做 区间求和,区间最值,区间更新,单点更新等操作

以区间和为例来做介绍:

建树

const int maxn=1000000+5;
struct Tree{
    int l,r;
    int val;
    int lazy,tag;//标记,需要区间更新时使用
}tree[maxn*2];
//把子节点的信息汇总到了父节点上,根据题目要求可做修改,比如可以更改为区间最值
void pushup(int cur)
{
	tree[cur].val = tree[2 * cur].val + tree[2 * cur + 1].val;
	//父节点是两个子节点的和,一直往上传值,就能使根节点是所有节点的和
}
void build(int l,int r,int step)
{
    int mid=(l+r)/2;
    tree[step].val=0;		 	//对节点初始化赋值,视情况看赋多少
    tree[step].l=l;
    tree[step].r=r;
    if(l==r){				 	//到达叶子节点
        tree[step].val=ary[l];	//赋值,这一步是对叶子节点的更新,若无需更新,则可以不写
        //ary数组为元素的初始值
        return;
    }
    //如果不是叶节点,就一直左右递归找,直到找到为止
    build(l,mid,step*2);
    build(mid+1,r,step*2+1);
    //将子节点的值汇总给父亲节点,用于求和
    pushup(step);
}

单点更新 hdu-1166

//单点值的更改,找指定的叶节点
//将tar节点的值增加val
void update(int tar, int val, int step)
{
	int l = tree[step].l, r = tree[step].r;
	if (l == r)
	{
		tree[step].val = tree[step].val + val;//节点更新,此处为增加,也可做换值处理
		return;
	}
	int mid = (l + r) / 2;
	if (tar <= mid)
		update(tar, val, 2 * step);
	else
		update(tar, val, 2 * step + 1);
	pushup(step);
}

区间更新

/************************************** poj-3468 ***************************************/
//区间加
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 100000 + 5
long long a[MAXN];
struct Tree{
	int left;
	int right;
	long long sum; 
	long long lazy;
}tree[MAXN << 2]; 
//把子节点汇集到父节点
void push_up(int step)
{
	tree[step].sum = tree[step << 1].sum + tree[step << 1 | 1].sum;
}
//区间加后向下更新
void push_down(int step)
{
	if (tree[step].lazy){
		tree[step << 1].sum += (tree[step << 1].right - tree[step << 1].left + 1)*tree[step].lazy;
		tree[step << 1 | 1].sum += (tree[step << 1 | 1].right - tree[step << 1 | 1].left + 1)*tree[step].lazy;
		tree[step << 1].lazy += tree[step].lazy;
		tree[step << 1 | 1].lazy += tree[step].lazy;
		tree[step].lazy = 0; 
	}
}
void BuildTree(int step, int l, int r) 
{
	tree[step].left = l;
	tree[step].right = r;
	tree[step].sum = 0;
	if (l == r)
		tree[step].sum = a[l];
	else{
		int mid = (l + r) >> 1;
		BuildTree(step << 1, l, mid);
		BuildTree(step << 1 | 1, mid + 1, r);
		push_up(step);
	}
	return;
}
long long query(int step, int l, int r){
	if (tree[step].left == l && tree[step].right == r)
		return tree[step].sum;
	push_down(step); 
	int mid = (tree[step].left + tree[step].right) >> 1;
	if (r <= mid)
		return query(step << 1, l, r);
	else if (l > mid)
		return query(step << 1 | 1, l, r);
	else
		return query(step << 1, l, mid) + query(step << 1 | 1, mid + 1, r);
}
void u_update(int step, int l, int r, int val) 
{
	if (tree[step].left == l && tree[step].right == r){
		tree[step].sum += 1LL * (r - l + 1)*val;
		tree[step].lazy += val;
		return;
	}
	else{
		int mid = (tree[step].left + tree[step].right) >> 1;
		push_down(step);
		if (r <= mid)
			u_update(step << 1, l, r, val);
		else if (l > mid)
			u_update(step << 1 | 1, l, r, val);
		else {
			u_update(step << 1, l, mid, val);
			u_update(step << 1 | 1, mid + 1, r, val);
		}
		push_up(step);
	}
	return;
}
int main()
{
	int n, m;
	int i;
	int l, r, val;
	char op[20];
	scanf("%d%d", &n, &m);
	for (i = 1; i <= n; i++)
		scanf("%lld", &a[i]);
	BuildTree(1, 1, n);
	while (m--){
		scanf("%s", op);
		if (op[0] == 'Q'){
			scanf("%d%d", &l, &r);
			printf("%lld\n", query(1, l, r));
		}
		else{
			scanf("%d%d%d", &l, &r, &val);
			u_update(1, l, r, val);
		}
	}
	return 0;
}


/********************************   hdu 1698  *************************************/
//将[l,r]区间内的值更换为val,需要用到标记
#include<stdio.h>
const int maxn = 10000000 + 5;
struct Tree {
	int l, r;
	int val;
}tree[maxn];
void build(int l, int r, int step)
{
	tree[step].l = l;
	tree[step].r = r;
	tree[step].val = 1;
	if (l == r)
		return;
	int mid = (l + r) / 2;
	build(l, mid, step * 2);
	build(mid + 1, r, step * 2 + 1);
}
void update(int l, int r, int val, int step)
{
	if (tree[step].l == l && tree[step].r == r) {
		tree[step].val = val;
		return;
	}
	if (tree[step].val != -1) {
		tree[step * 2].val = tree[step * 2 + 1].val = tree[step].val;
		tree[step].val = -1;
	}
	int mid = (tree[step].l + tree[step].r) / 2;
	if (r <= mid)
		update(l, r, val, step * 2);
	else if (l > mid)
		update(l, r, val, step * 2 + 1);
	else {
		update(l, mid, val, step * 2);
		update(mid + 1, r, val, step * 2 + 1);
	}
}
int query(int step)
{
	if (tree[step].val != -1)
		return (tree[step].r - tree[step].l + 1)*tree[step].val;
	else
		return query(step * 2) + query(step * 2 + 1);
}
int main()
{
	int t;
	scanf("%d", &t);
	for (int i = 1; i <= t; i++) {
		int n;
		scanf("%d", &n);
		build(1, n, 1);
		int m;
		scanf("%d", &m);
		while (m--) {
			int a, b, c;
			scanf("%d%d%d", &a, &b, &c);
			update(a, b, c, 1);
		}
		printf("Case %d: The total value of the hook is %d.\n", i, query(1));
	}
}

区间查询

//查询区间[ql,qr]里面元素的总和
int query(int ql,int qr,int cur)
{
    int l=tree[cur].l,r=tree[cur].r;
    if(ql<=l&&qr>=r)
        return tree[cur].val;
    int mid=(l+r)/2,res=0;
    if(ql<=mid)
        res+=query(ql,qr,2*cur);
    if(qr>mid)
        res+=query(ql,qr,2*cur+1);
    return res;
}

线段树的动态开点

**用处:**一般线段树开局直接4*N的空间,然而当N很大时,4倍空间会消耗很多,这时考虑用动态开点线段树,用多少开多少,跟c++的new差不多

**预备:**一个根节点root,存值数组c[N],左右儿子 lc[N],rc[N]

算法流程:
1.一开始只有一个根节点
2.update操作:类似串算法,每次传入节点root,区间范围[l,r]和更新点x,判断x在区间的哪边,在哪边就就往哪边走,如果遇到一个无标记的节点但又需要往这个节点走
就以访问次序给这个节点标号,像正常线段树更新即可。
3.query操作:和一般线段树不同的是,当我们遇到一个没有访问过的节点,直接返回,其他与线段树查询一致。

假设我们要对 1 3 5 7 8 9这段序列操作,区间范围[1,9] 开的线段树如下可以少点2,4,6这3个儿子节点
如果这段区间没有5,我们甚至可以不用开7号和8号节点

poj 2299

//以求逆序对为例
#include <iostream>
#include <algorithm>
#include <cmath>
#include<string.h>

using namespace std;

const int N = 5e6 + 10;

int root, n, c[N], lc[N], rc[N], cnt;

void update(int &p, int l, int r, int x, int val)
{
	if (!p) p = ++cnt; //遇到了,但没标号,标记
	if (l == r)
	{
		c[p] += val;
		return;
	}
	int mid = (l + r) >> 1;
	if (x <= mid) update(lc[p], l, mid, x, val);
	else update(rc[p], mid + 1, r, x, val);
	c[p] = c[lc[p]] + c[rc[p]];
}

int query(int p, int l, int r, int x, int y)
{
	if (!p) return 0;
	if (l == x && r == y) return c[p];
	int mid = (l + r) >> 1;
	if (y <= mid) return query(lc[p], l, mid, x, y);
	else if (x > mid) return query(rc[p], mid + 1, r, x, y);
	return query(lc[p], l, mid, x, mid) + query(rc[p], mid + 1, r, mid + 1, y);
}
int main()
{
	ios::sync_with_stdio(false);
	while (cin >> n && n) {
		memset(c, 0, sizeof(c));
		memset(rc, 0, sizeof(rc));
		memset(lc, 0, sizeof(lc));
		long long ans = 0;
		for (int i = 1; i <= n; i++)
		{
			int x;
			cin >> x;
			ans += query(root, 1, 1e9, x + 1, 1e9);
			update(root, 1, 1e9, x, 1);
		}
		cout << ans << endl;
	}
	return 0;
}
全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务