前缀和
应用:区间查询,不涉及数的变化。求区间[l,r]d的和
一维前缀和:
s[i] = a[1] + a[2] + … + a[i];
s[i] = s[i-1] + a[i];
二维前缀和:
s[i][j] = 第 i 行第 j 列格子左上部分所有元素的和
以(x1,y1)为左上角,(x2,y2)为右下角
s[x2,y2] - s[x2, y1-1] - s[x1-1, y2] + s[x1-1,y1-1]
差分
应用:前缀和的逆运算,给区间[l,r]中每个数加上c
一维差分:
B[l] += c, B[r+1] -= c
二维差分:
以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵中所有元素加上c
s[x1,y1] += c, s[x1, y2+1] -= c, s[x2+1,y1] -=c, s[x2+1,y2+1] += c
树状数组:
应用:树状数组作为线段树中的一部分,可以进行单点修改和区间查询。时间复杂度为O(logn)
树状数组、线段树裸题
给定 n 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列 [a,b] 的连续和。
输入格式 第一行包含两个整数 n 和 m,分别表示数的个数和操作次数。
第二行包含 n 个整数,表示完整数列。
接下来 m 行,每行包含三个整数 k,a,b (k=0,表示求子数列[a,b]的和;k=1,表示第 a 个数加 b)。
数列从 1 开始计数。
输出格式 输出若干行数字,表示 k=0 时,对应的子数列 [a,b] 的连续和。
注意点:
1.有三个核心函数。lowbit(int n) 返回数字n的最后一位1
2. query(int x) tr[x]表示的是(x-2^k, x]的和
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 100010;
int n, m;
int a[N];
int tr[N];
int lowbit(int x)
{
return x & -x;
}
int query(int x)
{
int res = 0;
for(int i=x; i; i-=lowbit(i)) res += tr[i];
return res;
}
void modify(int a, int b)
{
for(int i=a; i<=n; i+=lowbit(i)) tr[i] += b;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) scanf("%d",&a[i]);
for(int i=1; i<=n; i++) modify(i,a[i]);
int k, a, b;
while(m--)
{
scanf("%d%d%d",&k,&a,&b);
if(k == 0) printf("%d\n",query(b)-query(a-1));
else modify(a,b);
}
return 0;
}
线段树
应用:应用范围广。染色、面积等等(还没有学习到)。懒标记(只知道这个名词)
注意点:
1.数据范围4*N
2.很容易出错
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 100010;
int n, m;
int w[N];
struct Node{
int l, r;
int sum;
}tr[4*N];
void pushup(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void build(int u, int l, int r)
{
if(l == r) tr[u] = {l, r, w[r]};
else
{
tr[u] = {l,r};
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid+1, r);
pushup(u);
}
}
int query(int u, int l, int r)
{
if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
int sum = 0;
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) sum += query(u << 1, l, r);
if(r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
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;
if(x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) scanf("%d",&w[i]);
build(1,1,n);
int k, a, b;
while(m--)
{
scanf("%d%d%d",&k,&a,&b);
if(k == 0) printf("%d\n",query(1,a,b));
else modify(1, a, b);
}
return 0;
}