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!
  • 相关阅读:
    Java实现 蓝桥杯 算法提高 特等奖学金(暴力)
    Java实现 蓝桥杯 算法提高 特等奖学金(暴力)
    Java实现 蓝桥杯 算法提高 GPA(暴力)
    Java实现 蓝桥杯 算法提高 GPA(暴力)
    Java实现 蓝桥杯 算法提高 GPA(暴力)
    Java实现 蓝桥杯 算法提高 套正方形(暴力)
    Java实现 蓝桥杯 算法提高 套正方形(暴力)
    第一届云原生应用大赛火热报名中! helm install “一键安装”应用触手可及!
    云原生时代,2个方案轻松加速百万级镜像
    Knative 基本功能深入剖析:Knative Serving 自动扩缩容 Autoscaler
  • 原文地址:https://www.cnblogs.com/Go7338395/p/13471873.html
Copyright © 2011-2022 走看看