K - yousa的鸟蛋

K - yousa的鸟蛋

题目大意

给定一个 n × n n \times n n×n的二维平面,有修改和查询两种操作:给平面上某个矩形里的每一个数都加上 k k k,查询某个矩形里面所有数的和。

题解

其实这题可以用很多种方法来做,树套树。

不过二维树状数组不失为一种简单的方法,因为相比线段树树状数组的代码量更少。

  • 运用差分的思想:

在一维树状数组中,能实现区间修改操作,就是运用了差分 的思想,于是在二维树状数组上面,为了能实现区间修改操作,同样也是要用的差分思想

记差分数组 b i , j = a i , j − a i − 1 , j − a i , j − 1 + a i − 1 , j − 1 b_{i,j} = a_{i,j} - a_{i - 1,j} - a_{i, j- 1} + a_{i -1 , j - 1} bi,j=ai,jai1,jai,j1+ai1,j1

那么,我们就可以得出下面的式子:

s i , j = ∑ x = 1 i ∑ y = 1 j a x , y = ∑ x = 1 i ∑ y = 1 j ∑ x x = 1 x ∑ y y = 1 y b x x , y y s_{i,j}=\sum _{x=1}^i \sum_{y=1}^j a_{x,y}=\sum _{x=1}^i \sum_{y=1}^j \sum _{xx=1}^x \sum _{yy=1}^y b_{xx,yy} si,j=x=1iy=1jax,y=x=1iy=1jxx=1xyy=1ybxx,yy

与一维树状数组类似,交换 ∑ \sum 的次序:

∑ x x = 1 i ∑ x = x x i ∑ y y = 1 j ∑ y = y y j b x x , y y \sum_{xx=1}^i \sum_{x=xx}^i \sum_{yy = 1} ^ j \sum _{y = yy} ^ j b_{xx ,yy} xx=1ix=xxiyy=1jy=yyjbxx,yy

s i , j = ∑ x x = 1 i ∑ y y = 1 j ( i − x x + 1 ) × ( j − y y + 1 ) × b x x , y y s_{i,j}=\sum_{xx =1}^i \sum_{yy=1} ^j (i-xx+1)\times (j-yy+1) \times b_{xx,yy} si,j=xx=1iyy=1j(ixx+1)×(jyy+1)×bxx,yy

因此,我们只需要维护 ∑ b i , j \sum b_{i,j} bi,j ∑ b i , j \sum b_{i,j} bi,j ∑ b i , j \sum b_{i,j} bi,j ∑ b i , j \sum b_{i,j} bi,j就可以达到维护前缀和的目的了。

时间复杂度

一维树状数组的时间复杂度是 O ( l o g n ) O(log_n) O(logn)的,那么二维的就是 O ( l o g n × l o g m ) O(log_n \times log_m) O(logn×logm)

总的时间复杂度就是 O ( q × l o g n × l o g m ) O(q\times log_n \times log_m) O(q×logn×logm)

Tag

差分

二维树状数组

code

//#pragma GCC optimize (2)
//#pragma G++ optimize (2)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define G getchar
#define nxt(x) (x & (- x))
using namespace std;

int read()
{
    char ch;
    for(ch = G();(ch < '0' || ch > '9') && ch != '-';ch = G());
    int n = 0 , w;
    if (ch == '-')
    {
        w = -1;
        ch = G();
    } else w = 1;
    for(;'0' <= ch && ch <= '9';ch = G())n = (n<<1)+(n<<3)+ch-48;
    return n * w;
}

const int N = 1005;
long long s1[N][N] , s2[N][N] , s3[N][N] , s4[N][N] , ans;
int n , m , q , x , y , xx , yy , op , k , s[N][N];

void ins(int x , int y , int v )
{
    long long vv = (long long) (x - 1) * v;
    long long vvv = (long long) (y - 1) * v;
    long long vvvv = (long long) (x - 1) * (y - 1) * v;
    for (;x <= n ; x = x + nxt(x))
        for (int j = y ; j <=m ; j = j + nxt(j))
        {
            s1[x][j] += v;
            s2[x][j] += vv;
            s3[x][j] += vvv;
            s4[x][j] += vvvv;
        }
}

void work(int x , int y , int xx , int yy , int v)
{
    ins(x , y , v);
    ins(xx + 1 , y , - v);
    ins(x , yy + 1 , - v);
    ins(xx + 1 , yy + 1 , v);
}

long long sum(int x , int y)
{
    long long s = 0 ;
    for (int i = x ; i ; i = i - nxt(i))
        for (int j = y ; j ; j = j - nxt(j))
            s += s1[i][j] * x * y - y * s2[i][j] - x * s3[i][j] + s4[i][j];
    return s;
}

int main()
{
    freopen("k.in","r",stdin);
    //freopen("k.out","w",stdout);
    n = read();
    m = read();
    q = read();

    for (int i = 1; i <= n ; ++i)
        for (int j = 1; j <= m ; ++j)
        {
            s[i][j] = read();
            //work(i , j , i , j , s[i][j] - s[i - 1][j] - s[i][j - 1] + s[i - 1][j - 1]);
            work(i , j , i , j , s[i][j]);
        }

    for (int i = 0; i < q; ++i)
    {
        op = read();
        x = read();
        y = read();
        xx = read();
        yy = read();

        if (op == 1)
        {
            k = read();
            work(x , y , xx , yy , k);
        }
        else
        {
            ans = sum(xx , yy) - sum(x - 1 , yy) - sum(xx , y -1) + sum(x - 1 , y - 1);
            printf("%lld\n", ans);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值