题目:
输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
1 1 5
0 1 3
0 4 8
1 7 5
0 4 8
输出样例:
11
30
35
树状数组:
代码:
#include<cstdio>
#include<iostream>
using namespace std;
const int N=100010;
int n,m;
int a[N],tr[N];
//2^k
int lowbit(int x)
{
return x&-x;
}
//改变数组在位置x上的值(加上某个值)
void add(int x,int v)
{
for(int i=x;i<=n;i+=lowbit(i))tr[i]+=v;
}
//区间查询(求动态(在线)前缀和)
int query(int x)
{
int res=0;
for(int i=x;i>0;i-=lowbit(i))res+=tr[i];
return res;
}
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++)add(i,a[i]);//求得tr[i]
while(m--){
int k,a,b;
scanf("%d%d%d",&k,&a,&b);
if(k==1)add(a,b);
else
printf("%d\n",query(b)-query(a-1));//在线区间和
}
return 0;
}
线段树:
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n, m;
int w[N];
struct Node
{
int l, r;//左右区间
int sum;//区间值
}tr[N * 4];//创建树结点
//求结点大小(由左右儿子结点大小和得到)
void pushup(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
//初始化线段树各个结点(从根结点1开始依次往下递归至叶结点)
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);//为当前结点大小赋值
}
}
//区间查询(递归求在线区间和(u表示树当前结点位置,l、r表示要求的区间范围))
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;//结点区间二分
int sum = 0;
if (l <= mid) sum = query(u << 1, l, r);//与左儿子有交集
if (r > mid) sum += query(u << 1 | 1, l, r);//与右儿子有交集
return sum;
}
//单点修改(u表示树当前的结点位置,x为要修改的地方,v为要在x处加上的值)
void modify(int u, int x, int v)
{
if (tr[u].l == tr[u].r) tr[u].sum += v;//递归到了叶结点(即找到了要修改的位置x)
else
{
int mid = tr[u].l + tr[u].r >> 1;//树的结点区间二分
if (x <= mid) modify(u << 1, x, v);//x在左儿子部分
else modify(u << 1 | 1, x, v);//x在右儿子部分
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;
}
总结:
线段树和树状数组都是用来解决区间查询和更新的数据结构,但它们各自有不同的适用场景和问题解决能力。
线段树适用于解决静态的区间查询和更新问题,比如求区间最大值、最小值、区间和等。它可以高效地进行区间查询和更新操作,时间复杂度为O(log n)。线段树常用于解决动态规划、区间覆盖等问题。
树状数组适用于解决动态的单点查询和区间更新问题,比如求前缀和、区间和等。它可以高效地进行单点查询和区间更新操作,时间复杂度为O(log n)。树状数组常用于解决动态规划、离散化等问题。
总的来说,线段树适用于静态的区间查询和更新问题,而树状数组适用于动态的单点查询和区间更新问题。在实际应用中,可以根据具体的问题特点选择合适的数据结构来解决。