我不想再做笨蛋了|

wenli7363

园龄:3年3个月粉丝:7关注:6

【ACWING】 线段树和树状数组

线段树可以说包含树状数组,树状数组往往代码短,运行效率非常高
线段树和树状数组,下标都从1开始存
一般优先考虑树状数组。

1 树状数组 O(logn)

1.1 用处

本质上只有这两种作用,可以结合其他方法创造出更多用途(比如区间修改和单点查询)。

  1. 动态地给某个位置上的数+一个数 (单点修改)
  2. logn时间复杂度内,动态地,快速求前缀和。 (区间查询)

1

树状数组就是这样的结构。

其原理简单来说,就是根据一个数的二进制表示,末尾有几个零,那么就在第几层
比如说,X = 4 =(100),那么就在第三层
反正,A数组中的奇数都会存在C中的第0层

设A是下标从1开始的原数组,C是树状数组,存的是一段A数组的和
比如第K层的某个C数组C[x],他表示的范围是A数组中[x-2^k,x]的和
也就是C[x] = A数组中(x-lowbit(x),x]的和 左开右闭

//返回一个二进制表示的最后一个1,即返回2^k
int lowbit(int k){
return x & -x;
}

1.2 具体实现

1. 单点增值

void add(int x, int v)
{
for(int i = x; i <= n; i += lowbit(i))
C[i] += v;
}
//单点修改 比如令某点等于v
void add(int x, int v)
{
for(int i = x; i<=n; i+=lowbit(i))
C[i] = v - C[i];
}

2. 求前缀和

int query(int x)
{
int res = 0;
for(int i = x; i; i-= lowbit(i))
res += C[i];
return res;
}
// 查某个区间[l ,r]和
query(y) - query(x - 1);

2 线段树

线段树是将一个数组,构造成树的形式。
线段树比较复杂,一般出现得很少,尤其是出现懒标记的线段树,难度巨高

2.1 支持操作与复杂度分析

  1. 单点修改: O(logn)
  2. 区间查询: O(logn)

空间复杂度 O(4n),假设原数组长度为n,每个Node存储一个结构体

const int N = 10010;
struct Node{
int l, r;
int sum;
}tr[N*4];

存储方式,和堆存储一样
2

// 设当前节点为为x
父节点为 tr[x>>1];
左儿子;tr[x<<1];
右儿子:tr[x<<1 | 1];

2.2 具体操作

带懒标记的线段树会多一个操作 push down,我这里不包含这个

// push up函数,用子节点信息更新当前节点信息
void pushup(int u )
{
tr[u].sum = tr[u<<1].sum + tr[u<<1 | 1].sum;
}
//build 在一段区间上初始化线段树
// u为根节点编号,i,r是区间的左右边界
void build(int u, int l, int r)
{
// 如果是叶子节点
if(l == r) tr[u] = {l,r,w[l]};
// 如果不是叶子节点:二分+递归
else{
tr[u] = {l,r};
int mid = l + r >> 1; //注意 是求区间的中点
build(u<<1,l,mid), build(u<<1|1,mid+1,r);
pushup(u); //更新节点信息
}
}
//modify 修改
// u是根节点,x是目标值,v是增加的值
void modify(int u, int x, int v)
{
// 找到叶节点,更新
if(tr[u].l == tr[u].r) tr[u].sum += v;
// 递归寻找目标值
else{
int mid = tr[u].l + tr[u].r >> 1; //注意是求tr[u]的中点,和build区分
if(x <= mid) modify(u << 1, x, v);
` else modify(u<<1 | 1, x, v);
pushup(u);
}
}
// query 查询
int query(int u,int l, int r){
// 当前区间被完全包含了
if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
//递归算子区间
int mid = tr[u].l + tr[u].r >> 1; //注意是求tr[u]的中点,和build区分
int sum =0;
// 如果左边有交集
if(l <= mid) sum += query(u<<1,l,r); //注意是小于等于,那么下面那个只能大于
if(r > mid) sum += query(u<<1 | 1, l, r);
return sum;
}
posted @   wenli7363  阅读(64)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起