题目
给定 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列 的连续和。
输入格式
第一行包含两个整数 和 ,分别表示数的个数和操作次数。
第二行包含 个整数,表示完整数列。
接下来 行,每行包含三个整数 ( ,表示求子数列 的和; ,表示第 个数加 )。
数列从 开始计数。
输出格式
输出若干行数字,表示 时,对应的子数列 的连续和。
数据范围
,
,
,
数据保证在任何时候,数列中所有元素之和均在 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
解题
方法一:树状数组
树状数组
这里仅介绍树状数组的简单实现,详细介绍和证明见树状数组 - OI Wiki
简介
树状数组/二元索引树/Fenwick树是一种支持 单点修改 和 区间查询 的,代码量小的数据结构。
作用
- 单点修改( ):把某个位置上的数 自增 一个值。
- 区间查询( ):求序列中的某个 前缀和。
实现
可以获取 的二进制表示中最低位的 1
。若 的二进制表示中低位连续的 的个数为 ,那么 。观察上图可以发现: 在第 层,例如 所以 在第三层。
-
操作:
int lowbit(int n) { return n & -n; }
-
求 前缀和操作:
。int query(int i) { int sum = 0; for (; i > 0; i -= lowbit(i)) sum += tr[i]; return sum; }
-
修改操作:
从图中不难看出,假如修改 ,那么 会被修改,也就是说,如果修改位置 的数字,那么树状数组中 位置上的值会被更改。void add(int i, int x) { for (; i <= n; i += lowbit(i)) tr[i] += x; }
初始化树状数组的操作就相当于把 位置自增 ,直接使用修改操作增加即可。
思路
直接使用树状数组。
代码
import java.util.*;
import java.io.*;
public class Main {
static int n, m;
static int[] a, tr;
static int lowbit(int n) {
return n & -n;
}
static void add(int i, int x) {
for (; i <= n; i += lowbit(i)) tr[i] += x;
}
static int query(int i) {
int sum = 0;
for (; i > 0; i -= lowbit(i)) sum += tr[i];
return sum;
}
public static void main(String[] args) throws IOException {
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
in.nextToken();
n = (int) in.nval;
in.nextToken();
m = (int) in.nval;
a = new int[n + 1];
tr = new int[n + 1];
for (int i = 1; i <= n; ++i) {
in.nextToken();
a[i] = (int) in.nval;
}
for (int i = 1; i <= n; ++i) add(i, a[i]);
while (m-- > 0) {
in.nextToken();
int k = (int) in.nval;
in.nextToken();
int x = (int) in.nval;
in.nextToken();
int y = (int) in.nval;
if (k == 0) System.out.println(query(y) - query(x - 1));
else add(x, y);
}
}
}
方法二:线段树
思路
代码
import java.util.*;
import java.io.*;
public class Main {
static int n, m;
static int[] a;
static class Node {
int l, r, sum;
public Node(int l, int r, int sum) {
this.l = l;
this.r = r;
this.sum = sum;
}
}
static Node[] tr;
static void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
static void build(int u, int l, int r) {
if (l == r) tr[u] = new Node(l, r, a[r]);
else {
tr[u] = new Node(l, r, 0);
int m = l + r >> 1;
build(u << 1, l, m);
build(u << 1 | 1, m + 1, r);
pushup(u);
}
}
static int query(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
else {
int m = tr[u].l + tr[u].r >> 1;
int sum = 0;
if (l <= m) sum += query(u << 1, l, r);
if (r >= m + 1) sum += query(u << 1 | 1, l, r);
return sum;
}
}
static void modify(int u, int i, int x) {
if (tr[u].l == tr[u].r) tr[u].sum += x;
else {
int m = tr[u].l + tr[u].r >> 1;
if (i <= m) modify(u << 1, i, x);
else modify(u << 1 | 1, i, x);
pushup(u);
}
}
public static void main(String[] args) throws IOException {
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
in.nextToken();
n = (int) in.nval;
in.nextToken();
m = (int) in.nval;
a = new int[n + 1];
tr = new Node[n * 4];
for (int i = 1; i <= n; ++i) {
in.nextToken();
a[i] = (int) in.nval;
}
build(1, 1, n);
while (m-- > 0) {
in.nextToken();
int k = (int) in.nval;
in.nextToken();
int x = (int) in.nval;
in.nextToken();
int y = (int) in.nval;
if (k == 0) System.out.println(query(1, x, y));
else modify(1, x, y);
}
}
}
评论区