POJ 3468 A Simple Problem with Integers (分块解法)

缺乏、安全感 2021-12-15 11:09 329阅读 0赞

题意: 输入 n, m表初始有 n 个数, 接下来 m 行输入, Q x y 表示询问区间 [x, y]的和;

C x y z 表示区间 [x, y] 内所有数加上 z 。

分析:

我们将所给序列分成一个一个大小为根号n的块,每次询问时在线处理:

额外用到的数组:

pos[maxn]:pos[i]表示第i个元素属于哪个块。

add[maxn]:add[i]表示第i个块的增量。

sum[maxn]:sum[i]表示第i个块的总和。

L[maxn]:L[i]表示第i个块的左端点。

R[maxn]:R[i]表示第i个块的右端点。

一·询问操作 Q l r :

1.如果l和r都在一个块内,直接利用朴素算法从a[l]加到a[r]。

2.如果不一样,我们令p=pos[l],q=pos[r],l和r之间完整的块为从p+1到q-1,所以我们累加sum[i]+add*(R[i]-L[i]+1),i从p+1到q-1。

之后我们对于不完整的两端进行朴素处理。

二·改变操作 C l r val:

1.如果l和r都在一个块内,直接利用朴素算法从a[l]改变到a[r]。

2.如果不一样,我们令p=pos[l],q=pos[r],l和r之间完整的块为从p+1到q-1,所以我们改变add[i]+=val,i从p+1到q-1。

之后我们对于不完整的两端进行朴素处理。

总复杂度为O((N+M)*√N)。

代码:

  1. //参考《算法竞赛进阶指南》
  2. #include<iostream>
  3. #include<cstdio>
  4. #include<cstring>
  5. #include<algorithm>
  6. #include<cmath>
  7. using namespace std;
  8. long long a[100010], sum[100010], add[100010];
  9. int L[100010], R[100010]; // 每段左右端点
  10. int pos[100010]; // 每个位置属于哪一段
  11. int n, m, t;
  12. void change(int l, int r, long long d) {
  13. int p = pos[l], q = pos[r];
  14. if (p == q) {
  15. for (int i = l; i <= r; i++) a[i] += d;
  16. sum[p] += d*(r - l + 1);
  17. }
  18. else {
  19. for (int i = p + 1; i <= q - 1; i++) add[i] += d;
  20. for (int i = l; i <= R[p]; i++) a[i] += d;
  21. sum[p] += d*(R[p] - l + 1);
  22. for (int i = L[q]; i <= r; i++) a[i] += d;
  23. sum[q] += d*(r - L[q] + 1);
  24. }
  25. }
  26. long long ask(int l, int r) {
  27. int p = pos[l], q = pos[r];
  28. long long ans = 0;
  29. if (p == q) {
  30. for (int i = l; i <= r; i++) ans += a[i];
  31. ans += add[p] * (r - l + 1);
  32. }
  33. else {
  34. for (int i = p + 1; i <= q - 1; i++)
  35. ans += sum[i] + add[i] * (R[i] - L[i] + 1);
  36. for (int i = l; i <= R[p]; i++) ans += a[i];
  37. ans += add[p] * (R[p] - l + 1);
  38. for (int i = L[q]; i <= r; i++) ans += a[i];
  39. ans += add[q] * (r - L[q] + 1);
  40. }
  41. return ans;
  42. }
  43. int main() {
  44. cin >> n >> m;
  45. for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
  46. // 分块
  47. t = sqrt(n*1.0);
  48. for (int i = 1; i <= t; i++) {
  49. L[i] = (i - 1)*sqrt(n*1.0) + 1;
  50. R[i] = i*sqrt(n*1.0);
  51. }
  52. if (R[t] < n) t++, L[t] = R[t - 1] + 1, R[t] = n;
  53. // 预处理
  54. for (int i = 1; i <= t; i++)
  55. for (int j = L[i]; j <= R[i]; j++) {
  56. pos[j] = i;
  57. sum[i] += a[j];
  58. }
  59. // 指令
  60. while (m--) {
  61. char op[3];
  62. int l, r, d;
  63. scanf("%s%d%d", op, &l, &r);
  64. if (op[0] == 'C') {
  65. scanf("%d", &d);
  66. change(l, r, d);
  67. }
  68. else printf("%lld\n", ask(l, r));
  69. }
  70. }

发表评论

表情:
评论列表 (有 0 条评论,329人围观)

还没有评论,来说两句吧...

相关阅读