0
点赞
收藏
分享

微信扫一扫

区间修改 &查询算法

月半小夜曲_ 2022-03-13 阅读 59
算法

区间修改 & 查询

文章目录

树状数组

优势:单点修改,区间查询

时间复杂度: O ( log ⁡ n ) O (\log_n) O(logn)

存储原理

在这里插入图片描述

  • A:原数组
  • C:构造数组
  • 对于每一个C [i],共存 2 k 2^k 2k个元素,ki二进制下末尾0的个数

C [ 1   ( 1 ) ] = A [ 1 ]   C [ 2   ( 10 ) ] = A [ 1 ] + A [ 2 ] C [ 3   ( 11 ) ] = A [ 3 ] C [ 4   ( 100 ) ] = A [ 1 ] + A [ 2 ] + A [ 3 ] + A [ 4 ] C [ 5   ( 101 ) ] = A [ 5 ] C [ 6   ( 110 ) ] = A [ 5 ] + A [ 6 ] C [ 7   ( 111 ) ] = A [ 7 ] C [ 8   ( 1000 ) ] = A [ 1 ] + A [ 2 ] + A [ 3 ] + A [ 4 ] + A [ 5 ] + A [ 6 ] + A [ 7 ] + A [ 8 ] … … C [ i ] = A [ i − 2 k + 1 ] + A [ i − 2 k + 2 ] + … … + A [ i ] C [1 \ (1)] = A [1] \ \\ C [2 \ (10)] = A [1] + A [2] \\ C [3 \ (11)] = A [3] \\ C [4 \ (100)] = A [1] + A [2] + A [3] + A [4] \\ C [5 \ (101)] = A [5] \\ C [6 \ (110)] = A [5] + A [6] \\ C [7 \ (111)] = A [7] \\ C [8 \ (1000)] = A [1] + A [2] + A [3] + A [4] + A [5] + A [6] + A [7] + A [8] \\ …… \\ C [i] = A [i - 2 ^ k + 1] + A [i - 2 ^ k + 2] + …… + A [i] C[1 (1)]=A[1] C[2 (10)]=A[1]+A[2]C[3 (11)]=A[3]C[4 (100)]=A[1]+A[2]+A[3]+A[4]C[5 (101)]=A[5]C[6 (110)]=A[5]+A[6]C[7 (111)]=A[7]C[8 (1000)]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8]C[i]=A[i2k+1]+A[i2k+2]++A[i]

2 k 2 ^ k 2k求值(lowbit)

2 k = i   &   ( − i ) 2 ^ k = i \ \& \ (-i) 2k=i & (i)

  • x = 0, 0 & 0 = 0
  • x 为奇数,x & (-x) = 1
  • x 为偶数,且恰好为 2 n 2^n 2n,则x & (-x) = x
  • x 为偶数,但不为 2 n 2 ^ n 2n,则x = y * (2 ^ k) 。本质是把x用一奇数左移k位,因此x & (-x) = 2 ^ k
  • x 为偶数,结果为x中2的最大次方的因子,即lowbit,取2 ^ k

Code

单点修改
// x点加上v,维护C
void ADD (int x,int v)
{
    while (x <= n)
    {
        c [x] += v;
        x += lowbit (x);
    }
}

// 构造
for (int i = 1;i <= n;i ++)
{
    cin >> data;
    ADD (i,data);
}
区间查询
// 查询[1,x]和
int SUM (int x)
{
    int res;
    while (x)
    {
        res += c [x];
        x -= lowbit (x);
    }
    return res;
}
// 查询[l,r]和
res = SUM (r) - SUM (l - 1);

例题

P3374 【模板】树状数组 1 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)


线段树

优势:区间修改,单点查询

时间复杂度: log ⁡ n \log_n logn

储存原理

  • 二叉搜索树
  • tr [i].sum = tr [i * 2],sum + tr [i * 2 + 1].sum 结点 = 左子树 + 右子树
  • n >= 3时[1,n]的线段树可将[1,n]的任意子区间[L,R]分解为不超过 2 [ log ⁡ 2 ( n − 1 ) ] 2[\log_2(n - 1)] 2[log2(n1)]个子区间

Code

结构
struct TREE 
{
    int l,r,dat; // 左,右,和
    int lz;		 // lazytag
};
TREE tr [10001];

// 左子树
inline int TL(long long x)
{
	return x << 1;
}
// 右子树
inline int TR(long long x)
{
	return x << 1 | 1;
}
构造
// 起始点,左,右
void Build(long long i, long long l, long long r)
{
	tr[i].l = l;
	tr[i].r = r;
	if (l == r) // 为叶子节点,直接赋值
	{
		tr[i].dat = a[l];
		return;
	}
	long long mid = (l + r) >> 1;
    // Left
	Build(TL(i), l, mid);
    // Right
	Build(TR(i), mid + 1, r);
    
	tr[i].dat = tr[TL(i)].dat + tr[TR(i)].dat;
}
PushDown
  • 快速传递数据下去

void PushDown(long long i)
{
	if (tr[i].lz)
	{
		tr[TL(i)].lz += tr[i].lz;
		tr[TR(i)].lz += tr[i].lz;

		tr[TL(i)].dat += tr[i].lz * (tr[TL(i)].r - tr[TL(i)].l + 1);
		tr[TR(i)].dat += tr[i].lz * (tr[TR(i)].r - tr[TR(i)].l + 1);
		tr[i].lz = 0;
	}
}
区间修改
void Add (int i, int l, int r, int val)
{
	if (tr[i].l >= l && tr[i].r <= r)
	{
		tr[i].dat += val * (tr[i].r - tr[i].l + 1);
		tr[i].lz += val;
		return;
	}
	PushDown(i);
	long long mid = (tr[i].l + tr[i].r) >> 1;
	if (l <= mid) Add(TL(i), l, r, val);
	if (r > mid) Add(TR(i), l, r, val);
	tr[i].dat = tr[TL(i)].dat + tr[TR(i)].dat;
}
单点查询
int Find(int i, int pos)
{
	if (tr[i].l == pos && tr[i].r == pos)
		return tr[i].dat;
	PushDown(i);
	long long mid = (tr[i].l + tr[i].r) >> 1;
	if (pos <= mid) return Find(TL(i), pos);
	if (pos > mid) return Find(TR(i), pos);
}

例题

https://www.luogu.com.cn/problem/P3368


ST表

优势:区间求最值(RMQ)

储存原理

在这里插入图片描述

  • 倍增思想
  • f [i][j]表示从i位置开始的 2 j 2^j 2j个数中的最大值
  • 转移时可把点股权区间拆解成两个区间并分别取最大值:f [i] [j] = max (f [i] [j - 1], f [i + 2 ^ (j - 1)] [j - 1]);
  • 设原数组为a [N],开辟的f [N][31]表示从i位置开始的 2 j 2^j 2j个数中的最大值
  • f [i][0]表示元素本身,可初始化为f [i][0] = a [i]

Code

构造
void build ()
{
    // 直接赋值
	for (int i = 1;i <= n;i ++) 
		f [i][0] = a [i];
    
	for (int j = 1;j <= 30;j ++)
		for (int i = 1;i + (1 << j) - 1 <= n;i ++)
			f [i][j] = max (f [i][j - 1],f [i + (1 << (j - 1))][j - 1]); // 分别取最大值
}
查询

在这里插入图片描述

  1. 计算2 ^ k <= r - l + 1k
  2. 左端点和右端点分别查询,保证一定覆盖区间
  3. 结果为max (f [L][k], f [R - 2 ^ k][k])
int RMQ (int l,int r)
{
	int k = log2 (r - l + 1);
	return max (f [l][k],f [r - (1 << k) + 1][k]);
}

例题

P3865 【模板】ST 表 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

举报

相关推荐

分块 区间修改 区间查询

0 条评论