zoukankan      html  css  js  c++  java
  • 洛谷P4514 上帝造题的七分钟

    这是一个二维区域修改区域查询问题,可以考虑使用二维树状数组解决。

    我们先回忆一下一维区间修改区间查询树状数组是怎么做的,因为树状数组本身只支持求前缀和的形式,每次修改也只能单点修改,因此结合前缀和和单点修改的性质,我们想到可以用树状数组来维护差分数组,这样就能做到区间修改单点查询,但这还是不是我们需要的区间修改区间查询。上面的差分数组给了我们区间修改的启发,下面我们令 (d) 为差分数组,我们发现区间查询同样可以差分,于是我们的目标实际上是求这样一个式子((1 sim i) 的前缀和):

    [sumlimits_{i = 1} ^ n sumlimits_{j = 1} ^ i d_j ]

    可以考虑计算每个 (d_j) 对答案的贡献,即:

    [sumlimits_{i = 1} ^ n d_i imes (n - i + 1) ]

    为了能接下来我们让 (d_i)(i) 放在一起,将每次询问不同的 (n) 拿开方便维护:

    [(n + 1)sumlimits_{i = 1} ^ n d_i - sumlimits_{i = 1} ^ n d_i imes i ]

    于是我们惊奇地发现我们只需要维护出 (sumlimits_{i = 1} ^ n d_i) 以及 (sumlimits_{i = 1} ^ n d_i imes i) 即可。于是我们可以写出如下代码:

    void add(int p, int k){
        for(int i = p; i <= n; i += lowbit(i)) C1[i] += k, C2[i] += p * k;
    }
    int query(int p){
        int ans = 0;
        for(int i = p; i; i -= lowbit(i)) ans += (p + 1) * C1[i] - C2[i];
        return ans;
    }
    signed main(){
        n = read(), m = read();
        rep(i, 1, n) a[i] = read(), d[i] = a[i] - a[i - 1], add(i, d[i]);
        rep(i, 1, m){
            opt = read(), l = read(), r = read();
            if(opt == 1) k = read(), add(l, k), add(r + 1, -k);
            else printf("%lld
    ", query(r) - query(l - 1));
        }
        return 0;
    }
    

    同样类似于上面那个思路,我们可以定义一下二维数组的差分,即我们需要让 ((1, 1) sim (n, m)) 的前缀和为 (a_{i, j}),可以考虑二维前缀和的式子 (S_{i, j} = S_{i - 1, j} + S_{i, j - 1} - S_{i - 1, j - 1} + a_{i, j} = a_{i - 1, j} + a_{i, j - 1} - a_{i - 1, j - 1} + a_{i, j}),于是我们只需令差分数组 (d_{i, j} = a_{i, j} - a_{i - 1, j} - a_{i, j - 1} + a_{i - 1, j - 1}) 即可。不难发现我们每次对某个位置 ((i, j)) 进行修改,影响的是 ((i, j) sim (n, m)) 这一段区间,于是我们我们要给二维的一片区域 ((a, b) sim (c, d)) 加上某个数 (x),只需要在 ((a, b)) 出加上 (x),在 ((a, d + 1), (c + 1, b)) 出减去 (x),再在 ((c + 1, d + 1)) 处加上 (x) 即可。因为可以使用差分,因此我们每次需要查询的就是 ((1, 1) sim (n, m)) 这个区域的和,即:

    [sumlimits_{i = 1} ^ n sumlimits_{j = 1} ^ m sumlimits_{k = 1} ^ i sumlimits_{l = 1} ^ j d_{k, l} ]

    同样的我们考虑每个 (d_{k, l}) 对答案的贡献,显然 (d_{i, j}) 会被 ((i, j) sim (n, m)) 这个区域内的所有数算一次,于是原式即:

    [sumlimits_{i = 1} ^ n sumlimits_{j = 1} ^ m d_{i, j} imes (n - i + 1) imes (m - j + 1) ]

    [= sumlimits_{i = 1} ^ n sumlimits_{j = 1} ^ m d_{i, j} imes (nm + n + m + 1 - mi - i - nj - j + ij) ]

    同样让每个位置维护的值只与每个位置有关,将查询的部分提出来有:

    [(nm + n + m + 1) sumlimits_{i = 1} ^ n sumlimits_{j = 1} ^ m d_{i, j} - (m + 1)sumlimits_{i = 1} ^ n sumlimits_{j = 1} ^ m d_{i, j} imes i - (n + 1)sumlimits_{j = 1} ^ m d_{i, j} imes j + sumlimits_{j = 1} ^ m d_{i, j} imes ij ]

    于是我们每个位置维护四个值:(sumlimits_{i = 1} ^ n sumlimits_{j = 1} ^ m d_{i, j}, sumlimits_{j = 1} ^ m d_{i, j} imes i, sumlimits_{j = 1} ^ m d_{i, j} imes j, sumlimits_{j = 1} ^ m d_{i, j} imes ij) 即可。

    #include<bits/stdc++.h>
    using namespace std;
    #define N 2050 + 5
    #define lowbit(x) (x & (-x))
    char opt[N];
    int n, m, a, b, c, d, k, C[N][N][4];
    int read(){
        char c; int x = 0, f = 1;
        c = getchar();
        while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
    void add(int x, int y, int k){
        if(x < 1 || x > n || y < 1 || y > m) return;
        int a = k * x, b = k * y, c = k * x * y;
        for(int i = x; i <= n; i += lowbit(i))
            for(int j = y; j <= m; j += lowbit(j))
                C[i][j][0] += k, C[i][j][1] += a, C[i][j][2] += b, C[i][j][3] += c;
    }
    int query(int x, int y){
        if(x < 1 || x > n || y < 1 || y > m) return 0;
        int a = 0, b = 0, c = 0, d = 0;
        for(int i = x; i; i -= lowbit(i))
            for(int j = y; j; j -= lowbit(j))
                a += C[i][j][0], b += C[i][j][1], c += C[i][j][2], d += C[i][j][3];
        return a * (x * y + x + y + 1) - b * (y + 1) - c * (x + 1) + d;
    }
    int main(){
        n = read(), m = read();
        while(scanf("%s", opt + 1) != EOF){
            a = read(), b = read(), c = read(), d = read();
            if(opt[1] == 'L'){
                k = read();
                add(a, b, k), add(c + 1, d + 1, k);
                add(a, d + 1, -k), add(c + 1, b, -k);
            }
            else printf("%d
    ", query(c, d) - query(c, b - 1) - query(a - 1, d) + query(a - 1, b - 1));
        }
        return 0;
    }
    
    GO!
  • 相关阅读:
    Redis面试总结
    文件上传文件的权限lnmp 环境配置,尤其整个项目复制过来
    linux cat /etc/passwd 说明
    php上传文件与图片到七牛的实例详解
    一起谈.NET技术,参数编码 完全解决方案 狼人:
    一起谈.NET技术,在.NET中使用域对象持续模式 狼人:
    一起谈.NET技术,从.NET中委托写法的演变谈开去(中):Lambda表达式及其优势 狼人:
    一起谈.NET技术,从扩展方法到流畅的程序体验(一) 狼人:
    一起谈.NET技术,构建高性能ASP.NET站点之一 剖析页面的处理过程(前端) 狼人:
    一起谈.NET技术,ASP.NET MVC 2 验证消息本地化策略扩展 狼人:
  • 原文地址:https://www.cnblogs.com/Go7338395/p/13471873.html
Copyright © 2011-2022 走看看