一 问题描述
有 N 个数,A1 A2 An,需要对其进行两种操作,一种操作是对给定区域中的每个数据都添加一个给定的数,另一个操作是查询给定区间中数的总和。
二 输入和输出
1 输入
第 1 行包含两个数 N 和 Q,第 2 行,包括 N 个数,为 A1 A2 An 的初始值。接下来的 Q 行,每行都表示一种操作。
C a b c :将 Aa Aa+1 ... Ab 中的每一个数都加 c
Q a b:查询 Aa Aa+1 ... Ab 的总和
2 输出
对于每个查询都单行输出区间和的值。
三 输入和输出样例
1 输入样例
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
2 输出样例
4
55
9
15
四 分析和设计
1 分析
本问题有两种操作:区间更新和区间查询,可采用分块算法解决。
2 算法设计
a 分块预处理
将序列分块,然后对每个块都标记左右端点 L[i] 和 R[i],对最后一块需要特殊处理;标记每个元素所属的块,累加每一块的和值。
b 区间更新
首先取 l 和 r 所属的块,p=pos[l],q=pos[r];若属于同一块,则对该区间的所有元素都进行暴力修改,同时更新该块的和值。若不属于同一块,则对中间完全覆盖的块打上懒标记,add[i]+=d;对首尾两端的元素暴力修改即可。
c 区间查询
首先取 l 和 r 所属的块,p=pos[l],q=pos[r];若属于同一块,则对该区间的所有元素都进行暴力累加,然后加上懒标记上的值。若不属于同一块,则对中间完全覆盖的块累加 sum[] 值和懒标记上的值,然后对首尾两端的元素暴力累加元素值及懒标记值。
五 代码
package com.platform.modules.alg.alglib.poj3468;
public class Poj3468 {
public String output = "";
private int N = 100010;
private long a[] = new long[N];
private long sum[] = new long[N];
private long add[] = new long[N];
private int L[] = new int[N];
private int R[] = new int[N];
private int d;
private int pos[] = new int[N];
int n;
int m;
int t;
int l;
int r;
char op[] = new char[3];
// 预处理
void build() {
t = (int) Math.sqrt(n * 1.0);
int num = n / t;
if (n % t != 0) num++;
for (int i = 1; i <= num; i++) {
// 每块的左右
L[i] = (i - 1) * t + 1;
R[i] = i * t;
}
R[num] = n;
for (int i = 1; i <= num; i++)
for (int j = L[i]; j <= R[i]; j++) {
pos[j] = i; // 表示属于哪个块
sum[i] += a[j]; // 计算每块和值
}
}
// 区间[l,r]加上d
void change(int l, int r, long d) {
int p = pos[l], q = pos[r]; // 读所属块
if (p == q) { // 在一块中
for (int i = l; i <= r; i++) // 暴力修改
a[i] += d;
sum[p] += d * (r - l + 1); // 修改和值
} else {
for (int i = p + 1; i <= q - 1; i++) // 中间完全覆盖块打懒标记
add[i] += d;
for (int i = l; i <= R[p]; i++) // 左端暴力修改
a[i] += d;
sum[p] += d * (R[p] - l + 1);
for (int i = L[q]; i <= r; i++) // 右端暴力修改
a[i] += d;
sum[q] += d * (r - L[q] + 1);
}
}
// 区间查询
long query(int l, int r) {
int p = pos[l], q = pos[r];
long ans = 0;
if (p == q) { // 在一块中
for (int i = l; i <= r; i++) // 累加
ans += a[i];
ans += add[p] * (r - l + 1); // 计算懒标记
} else {
for (int i = p + 1; i <= q - 1; i++) // 累加中间块
ans += sum[i] + add[i] * (R[i] - L[i] + 1);
for (int i = l; i <= R[p]; i++) // 左端暴力累加
ans += a[i];
ans += add[p] * (R[p] - l + 1);
for (int i = L[q]; i <= r; i++) // 右端暴力累加
ans += a[i];
ans += add[q] * (r - L[q] + 1);
}
return ans;
}
public String cal(String input) {
String[] line = input.split("\n");
String[] nums = line[0].split(" ");
n = Integer.parseInt(nums[0]);
m = Integer.parseInt(nums[1]);
String[] words = line[1].split(" ");
for (int i = 1; i <= n; i++)
a[i] = Integer.parseInt(words[i - 1]);
build();
for (int i = 1; i <= m; i++) {
String[] command = line[i + 1].split(" ");
l = Integer.parseInt(command[1]);
r = Integer.parseInt(command[2]);
if (command[0].charAt(0) == 'C') {
d = Integer.parseInt(command[3]);
change(l, r, d);
} else
output += query(l, r) + "\n";
}
return output;
}
}