zoukankan      html  css  js  c++  java
  • [bzoj3132]上帝造题的七分钟——二维树状数组

    题目大意

    你需要实现一种数据结构,支援以下操作。

    1. 给一个矩阵的子矩阵的所有元素同时加一个数。
    2. 计算子矩阵和。

    题解

    一看这个题,我就首先想到用线段树套线段树做。

    使用二维线段树的错误解法

    其实是第一次写二维线段树orz。为了方便,我们不再使用k<<1作为左儿子,k<<1|1作为右儿子,而是使用一个lc,rc数组来存储左孩子和右孩子。实现起来不是很麻烦但是有许多细节。我们可以在操作函数中写一个参数表示是操作的行还是操作的列。另外注意打标记。对于节点k,标记一旦下传,他的孩子的各种标记即被修改,而它自身的标记应该在更前面的一个时刻修改。同时注意update,因为这个WA了好多次。尽管这样,还是有许多错。小数据基本可以过,但大数据就不行了Orz,求各位大佬帮助修改。

    使用二维树状数组的正解

    首先我们先要把树状数组(单点修改)扩展的二维。很显然,我们可以直接在两个维度重现一维的操作,非常简单。
    那么,对于区间修改,我们也要扩展到二维,我们回忆一维时候的做法,我们首先分类讨论,比较了修改一个区间对于一个前缀的影响,具体可以见《挑战程序设计竞赛》或者我的模板汇总贴。
    我们继续这样考虑。记(g(i,j))为修改后的二维前缀和,(s(i,j))为修改前的二维前缀和。我们分类讨论,容易得出以下结论。

    [g(i,j) = s(i,j) ]

    [g(i,j) = s(i,j) + (ij+(x_0-1)(y_0-1))k - (j(x_0-1)+i(y_0-1))k ]

    [g(i,j) = s(i,j) + (iy_1+(x_0-1)(y_0-1))k - ((x_0-1)y_1+i(y_0-1))k ]

    [g(i,j) = s(i,j) + (jx_1+(x_0-1)(y_0-1))k - (j(x_0-1)+x_1(y_0-1))k ]

    [g(i,j) = s(i,j) + (x_1y_1+(x_0-1)(y_0-1))k - ((x_0-1)y_1+x_1(y_0-1))k ]

    上面的五个式子分别对应ij均小,ij均ok,i好j大,i大j好,i大j大五种情况。//上面的狮子(x_0y_0)均-1的原因是本蒟蒻开始推的时候忘记把区间前界-1了Orz
    推演到这里就非常方便了。我们无论是使用差分的思想,还是使用乱搞的思想都非常好搞了。
    具体地,我们开四个树状数组,分别表示与i,j均无关,与ij一个有关,与ij均有关四种情况,分清楚每一种情况的作用域,准确地运用差分即可。
    其实,程序实现的时候有很多细节,一个是因为推导时候的错误,一个是因为树状数组从1开始存储,所以要把每个坐标都加一。
    这个题写了一晚上,但AC了还是非常有成就感QAQ

    代码(使用二维线段树的错误解答)

    #include <algorithm>
    #include <cstdio>
    const int maxm = 3000;
    const int maxn = 2048 * 2048 * 4;
    int sum[maxn], ll[maxn], rr[maxn], add[maxn], lc[maxn], rc[maxn], sz = 0,
                                                                      gg[maxm];
    char wtf;
    int n, m;
    void pushdown(int k) {
      if (add[k]) {
        add[lc[k]] += add[k];
        add[rc[k]] += add[k];
        int mid = (ll[k] + rr[k]) >> 1;
        sum[lc[k]] += (mid - ll[k] + 1) * (add[k]);
        sum[rc[k]] += (rr[k] - mid) * (add[k]);
        add[k] = 0;
      }
      return;
    }
    void update(int k) { sum[k] = sum[lc[k]] + sum[rc[k]]; }
    int build(int k, int l, int r, int a) {
      ll[k] = l, rr[k] = r;
      if (l == r) {
        if (a == 0) {
          gg[k] = ++sz;
          build(gg[k], 1, m, 1);
        } else if (a == 1) {
          sum[k] = 0;
        }
        return k;
      }
      int mid = (l + r) >> 1, left = build(++sz, l, mid, a),
          right = build(++sz, mid + 1, r, a);
      lc[k] = left, rc[k] = right;
      sum[k] = sum[lc[k]] + sum[rc[k]];
      return k;
    }
    void change(int k, int y0, int y1, int val) {
      int l = ll[k], r = rr[k], mid = (l + r) >> 1;
      if (y0 <= l && r <= y1) {
        sum[k] += (r - l + 1) * (val);
        add[k] = val;
        return;
      }
      pushdown(k);
      if (y0 <= mid)
        change(lc[k], y0, y1, val);
      if (y1 > mid)
        change(rc[k], y0, y1, val);
      update(k);
    }
    void plus(int k, int x0, int x1, int y0, int y1, int val) {
      int l = ll[k], r = rr[k], mid = (l + r) >> 1;
      if (x0 <= l && r <= x1 && l == r) {
        change(gg[k], y0, y1, val);
        return;
      }
      if (x0 <= mid)
        plus(lc[k], x0, x1, y0, y1, val);
      if (x1 > mid)
        plus(rc[k], x0, x1, y0, y1, val);
      update(k);
      return;
    }
    int query(int k, int x0, int x1, int y0, int y1, int a) {
      int l = ll[k], r = rr[k], mid = (l + r) >> 1;
      if (a == 0) {
        if (x0 <= l && r <= x1 && l == r) {
          return query(gg[k], x0, x1, y0, y1, 1);
        }
        int ans = 0;
        if (x0 <= mid)
          ans += query(lc[k], x0, x1, y0, y1, 0);
        if (x1 > mid)
          ans += query(rc[k], x0, x1, y0, y1, 0);
        return ans;
      }
      if (a == 1) {
        if (y0 <= l && r <= y1) {
          return sum[k];
        }
        pushdown(k);
        int ans = 0;
        if (y0 <= mid)
          ans += query(lc[k], x0, x1, y0, y1, 1);
        if (y1 > mid)
          ans += query(rc[k], x0, x1, y0, y1, 1);
        update(k);
        return ans;
      }
    }
    int main() {
    #ifdef D
      freopen("input", "r", stdin);
    #endif
      scanf("%c %d %d", &wtf, &n, &m);
      int t = build(++sz, 1, n, 0);
      char ch;
      while (scanf("%c", &ch) == 1) {
        if (ch == 'L') {
          int x0, y0, x1, y1, val;
          scanf("%d %d %d %d %d", &x0, &y0, &x1, &y1, &val);
          if (x0 > x1) {
            std::swap(x0, x1);
            std::swap(y0, y1);
          }
          plus(t, x0, x1, y0, y1, val);
        }
        if (ch == 'k') {
          int x0, y0, x1, y1;
          scanf("%d %d %d %d", &x0, &y0, &x1, &y1);
          if (x0 > x1 && y0 < y1) {
            std::swap(y0, y1);
          }
          if (x0 > x1)
            std::swap(x0, x1);
          int ans = query(t, x0, x1, y0, y1, 0);
          printf("%d
    ", ans);
        }
      }
      return 0;
    }
    

    代码(使用树状数组的正解)

    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #ifdef D
    const int maxn = 30;
    #else
    const int maxn = 2052;
    #endif
    int n, m;
    int c
        [4][maxn]
        [maxn]; // a:与i无关与j无关,b:与i有关与j无关,c:与i无关与j有关,d:与i有关与j有关
    char opt;
    void change(int id, int x, int y, int val) {
      for (int i = x; i <= n; i += i & -i) {
        for (int j = y; j <= m; j += j & -j) {
          c[id][i][j] += val;
        }
      }
    }
    int qu(int id, int x, int y) {
      int ans = 0;
      for (int i = x; i > 0; i -= i & -i) {
        for (int j = y; j > 0; j -= j & -j) {
          ans += c[id][i][j];
        }
      }
      return ans;
    }
    void pls(int x0, int y0, int x1, int y1, int k) {
      change(0, x0 + 1, y0 + 1, (x0 * y0) * k);
      change(0, x0 + 1, y1 + 1, -(x0 * y1) * k);
      change(0, x1 + 1, y0 + 1, -(x1 * y0) * k);
      change(0, x1 + 1, y1 + 1, (x1 * y1) * k);
      //-------
      change(1, x0 + 1, y0 + 1, -(y0 * k));
      change(1, x1 + 1, y0 + 1, (y0 * k));
      change(1, x0 + 1, y1 + 1, (y1 * k));
      change(1, x1 + 1, y1 + 1, -(y1 * k));
      //-------
      change(2, x0 + 1, y0 + 1, -(x0 * k));
      change(2, x0 + 1, y1 + 1, (x0 * k));
      change(2, x1 + 1, y0 + 1, (x1 * k));
      change(2, x1 + 1, y1 + 1, -(x1 * k));
      //-------
      change(3, x0 + 1, y0 + 1, k);
      change(3, x0 + 1, y1 + 1, -k);
      change(3, x1 + 1, y0 + 1, -k);
      change(3, x1 + 1, y1 + 1, k);
    }
    int sum(int x, int y) {
      return qu(0, x, y) + (qu(1, x, y) * x) + (qu(2, x, y) * y) +
             (qu(3, x, y) * x * y);
    }
    int query(int x0, int y0, int x1, int y1) {
      int sum1 = sum(x0 - 1, y0 - 1);
      int sum2 = sum(x0 - 1, y1);
      int sum3 = sum(x1, y0 - 1);
      int sum4 = sum(x1, y1);
      return sum4 - sum2 - sum3 + sum1;
    }
    int main() {
    #ifdef D
      freopen("input", "r", stdin);
    #endif
      scanf("%c %d %d", &opt, &n, &m);
      n++, m++;
      memset(c, 0, sizeof(c));
      while (scanf("%c", &opt) == 1)
        if (opt == 'L' || opt == 'k') {
          int x0, y0, x1, y1;
          scanf("%d %d %d %d", &x0, &y0, &x1, &y1);
          x0++;
          x1++;
          y0++;
          y1++;
          if (x0 > x1) {
            std::swap(x0, x1);
            std::swap(y0, y1);
          }
          if (opt == 'L') {
            int v;
            scanf("%d ", &v);
            x0--, y0--;
            pls(x0, y0, x1, y1, v);
          }
          if (opt == 'k') {
    #ifdef D
            for (int k = 0; k < 4; k++) {
              for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= m; j++)
                  std::cout << c[k][i][j] << ' ';
                std::cout << std::endl;
              }
              std::cout << std::endl << std::endl;
            }
    #endif
            int ans = query(x0, y0, x1, y1);
            printf("%d
    ", ans);
          }
        }
    }
    
  • 相关阅读:
    Oracle中的4大空值处理函数用法举例
    PyCharm安装
    Python安装与环境变量的配置
    多层分组排序问题
    将时间点的数据变成时间段的数据
    根据状态变化情况,求最大值和最小值
    ubuntu 源码安装 swig
    CSDN博客排名第一名,何许人也
    thinkPHP的常用配置项
    拔一拔 ExtJS 3.4 里你遇到的没遇到的 BUG(1)
  • 原文地址:https://www.cnblogs.com/gengchen/p/6498564.html
Copyright © 2011-2022 走看看