给定 n个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列 [a,b]
的连续和。
输入格式
第一行包含两个整数 n和 m,分别表示数的个数和操作次数。
第二行包含 n个整数,表示完整数列。
接下来 m行,每行包含三个整数 k,a,b(k=0,表示求子数列[a,b]的和;k=1,表示第 a
个数加 b)。
数列从 1 开始计数。
输出格式
输出若干行数字,表示 k=0时,对应的子数列 [a,b]的连续和。
数据范围
1≤n≤100000,
1≤m≤100000,
1≤a≤b≤n,
数据保证在任何时候,数列中所有元素之和均在 int 范围内。
输入样例:
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
难度:简单
时/空限制:1s / 64MB
来源:《信息学奥赛一本通》,模板
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
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[l]}; //叶节点直接赋值
else
{
tr[u] = {l, r, 0}; //先附上左右边界的初值
int mid = l + r >> 1;
build(u << 1, l, mid); //左儿子建树
build(u << 1 | 1, mid + 1, r); //右儿子建树
pushup(u); //左右儿子建完树再更新当前节点的sum
}
}
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; // 注意不是l,r 而是当前线段的中点
int res = 0;
if(mid >= l) res += query(u << 1, l, r); //当前线段的左半边与目标区间有交集
if(r >= mid + 1) res += query(u << 1 | 1, l, r); //当前线段的右半边与目标区间有交集
//query的后两个参数始终是l,r
return res;
}
void modify(int u, int x, int v) //当前节点位置,要修改的位置,要增加的值
{
if(tr[u].l == tr[u].r) tr[u].sum += v; //如果当前已经是叶节点了,那就直接让他的总和加上 v 就可以了
else
{
//找一下目标点在线段的左半边还是右半边
int mid = tr[u].l + tr[u].r >> 1;
if(mid >= x) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v); //左半边找左二子,右半边找右儿子
pushup(u); // 修改完当前节点完要pushup一遍,叶节点不要pushup会变0的
}
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++) scanf("%d", &w[i]);
build(1, 1, n);
while(m --)
{
int k , a, b;
scanf("%d%d%d", &k, &a, &b);
if(k == 0) printf("%d\n", query(1, a, b));
else modify(1, a, b);
}
return 0;
}