二维差分·学习备忘录

二维差分

为什么我为OI泪目?因为我菜得离谱…

引入

一维差分用来O(1)修改区间,配合上一维前缀和就是O(N)的查询区间和。
差分为前缀和的逆运算。
二维差分同理。
接下来这道题就用二维差分来解决。

例题:地毯 > > 例题:地毯>> 例题:地毯>>

地毯

题目描述

n × n n\times n n×n 的格子上有 m m m 个地毯。

给出这些地毯的信息,问每个点被多少个地毯覆盖。

输入格式

第一行,两个正整数 n , m n,m n,m。意义如题所述。

接下来 m m m 行,每行两个坐标 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) ( x 2 , y 2 ) (x_2,y_2) (x2,y2),代表一块地毯,左上角是 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),右下角是 ( x 2 , y 2 ) (x_2,y_2) (x2,y2)

输出格式

输出 n n n 行,每行 n n n 个正整数。

i i i 行第 j j j 列的正整数表示 ( i , j ) (i,j) (i,j) 这个格子被多少个地毯覆盖。

样例 #1
样例输入 #1
5 3
2 2 3 3
3 3 5 5
1 2 1 4
样例输出 #1
0 1 1 1 0
0 1 1 0 0
0 1 2 1 1
0 0 1 1 1
0 0 1 1 1
提示
样例解释

覆盖第一个地毯后:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 1 1 0 0 0 0 0 0
0 0 0 1 1 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

覆盖第一、二个地毯后:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 1 1 0 0 0 0 0 0
0 0 0 1 1 1 2 2 2 1 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1

覆盖所有地毯后:

0 0 0 1 1 1 1 1 1 1 1 1 0 0 0
0 0 0 1 1 1 1 1 1 0 0 0 0 0 0
0 0 0 1 1 1 2 2 2 1 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1

数据范围

对于 20 % 20\% 20% 的数据,有 n ≤ 50 n\le 50 n50 m ≤ 100 m\le 100 m100

对于 100 % 100\% 100% 的数据,有 n , m ≤ 1000 n,m\le 1000 n,m1000

题解

康康数据范围, n < = 1000 n<=1000 n<=1000,暴力修改 O ( n 2 ) O(n^ 2) O(n2),共有 O ( m ) O(m) O(m)次修改,总共 O ( n 2 ∗ m ) = 1 e 9 O(n^2*m)=1e9 O(n2m)=1e9复杂度,洛谷能够过,但 C C F CCF CCF肯定会把你卡死。
于是出现吧:二维差分!
首先拿来一张图,要让红色部分+1,如何 O ( 1 ) O(1) O(1)解决?
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0

0 0 0 0
0 0(x1,y1) 0 0
0 0 0(x2,y2) 0
0 0 0 0
差分是为了后面的前缀和做准备
我们先给 ( x 1 , y 1 ) + 1 (x1,y1)+1 (x1,y1)+1,可以达成增加的目的,但事情就会成这样(前缀和之后)
0 0 0 0
0 1 1 1
0 1 1 1
0 1 1 1

很显然,蓝,绿,棕四个部分都是被多加了的
于是我们再给 ( x 1 , y 2 + 1 ) − 1 , ( x 2 + 1 , y 1 ) − 1 (x1,y2+1)-1,(x2+1,y1)-1 (x1,y2+1)1,(x2+1,y1)1来抵消影响。
但……
0 0 0 0
0 1 1 0
0 1 1 0
0 0 0 -1
这里会被减两遍
再给其+1就OK

如果遇到差分数组要初始化的情况,例如第2题,我们就将它当做边长为1的矩形差分处理即可。

CODE

#include<bits/stdc++.h>
using namespace std;
const int tsn=1e3+5;

int n,m;
int qz[tsn][tsn];
int sum[tsn][tsn];

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("00in.txt","r",stdin);
        freopen("00out.txt","w",stdout);
    #endif
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int a1,b1,a2,b2;
        cin>>a1>>b1>>a2>>b2;
        qz[a1][b1]++,qz[a1][b2+1]--,qz[a2+1][b1]--,qz[a2+1][b2+1]++;
    }
    // for(int i=1;i<=n;i++,cout<<endl)
    //     for(int j=1;j<=n;j++)
    //         cout<<qz[i][j]<<" ";cout<<endl;
    for(int i=1;i<=n;i++,cout<<endl)
        for(int j=1;j<=n;j++)
            sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+qz[i][j],cout<<sum[i][j]<<" ";
    return 0;
}


【模板】二维差分

题目描述

给你一个n行m列的矩阵,下标从1开始。

接下来有q次操作,每次操作输入5个参数x1, y1, x2, y2, k

表示把以(x1, y1)为左上角,(x2,y2)为右下角的子矩阵的每个元素都加上k,

请输出操作后的矩阵。

输入格式

第一行包含三个整数 n , m , q n,m,q n,m,q.

接下来 n n n行,每行 m m m个整数,代表矩阵的元素

接下来 q q q行,每行5个整数 x 1 , y 1 , x 2 , y 2 , k x1, y1, x2, y2, k x1,y1,x2,y2,k,分别代表这次操作的参数

输出格式

输出n行,每行m个数,每个数用空格分开,表示这个矩阵。

样例 #1
样例输入 #1
2 3 4
1 2 3
4 5 6
1 1 2 2 3
1 2 2 3 -1
1 1 1 3 4
1 1 2 1 1
样例输出 #1
9 8 6
8 7 5
提示

1 ≤ n , m ≤ 1000 1≤n,m≤1000 1n,m1000

1 ≤ q ≤ 1 0 5 1≤q≤10^5 1q105

1 ≤ x 1 ≤ x 2 ≤ n 1≤x1≤x2≤n 1x1x2n

1 ≤ y 1 ≤ y 2 ≤ m 1≤y1≤y2≤m 1y1y2m

− 1 0 9 ≤ 矩阵中的元素 ≤ 1 0 9 −10^9≤矩阵中的元素≤10^9 109矩阵中的元素109

− 1 0 5 ≤ k ≤ 1 0 5 −10^5≤k≤10^5 105k105

分析

注意一下上文,对你来说不是难事。

CODE

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int tsn=1e3+5;

int n,m,q;
int a[tsn][tsn];
int cf[tsn][tsn];
int sum[tsn][tsn];
void _cf(int a1,int b1,int a2,int b2,int k)
{
    cf[a1][b1]+=k,
    cf[a2+1][b1]-=k,
    cf[a1][b2+1]-=k,
    cf[a2+1][b2+1]+=k;
}
signed main()
{
    #ifndef ONLINE_JUDGE
        freopen("00in.txt","r",stdin);
        freopen("00out.txt","w",stdout);
    #endif
    
    int n,m,q;
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            cin>>a[i][j],_cf(i,j,i,j,a[i][j]);
    while(q--)
    {
        int a1,b1,a2,b2,k;
        cin>>a1>>b1>>a2>>b2>>k;
        _cf(a1,b1,a2,b2,k);
    }
    for(int i=1;i<=n;i++,cout<<endl)
        for(int j=1;j<=m;j++)
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+cf[i][j],cout<<sum[i][j]<<" ";
    return 0;
}

完结撒❀

在这里插入图片描述
在这里插入图片描述

图片来自网络。

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二维差分是一种常用的数据结构算法巧,用于高效地处理二维矩阵区间的更新和查询操作。它可以在O(1)的时间复杂度内完成区间的更新和查询操作,相比传统的暴力遍历方法,具有更高的效率。 二维差分的基本思想是将原始矩阵转化为一个差分矩阵,差分矩阵中的每个元素表示原始矩阵中相邻元素之间的差值。通过对差分矩阵进行预处理,可以实现对原始矩阵区间的更新和查询操作。 具体来说,二维差分的操作包括两个步骤:预处理和操作。预处理阶段,需要根据原始矩阵构建差分矩阵;操作阶段,可以通过对差分矩阵的更新来实现对原始矩阵区间的更新,同时可以通过对差分矩阵的求和来实现对原始矩阵区间的查询。 下面是二维差分的基本操作: 1. 构建差分矩阵:对于原始矩阵A,构建一个差分矩阵B,其中B[i][j] = A[i][j] - A[i-1][j] - A[i][j-1] + A[i-1][j-1]。 2. 区间更新:对于原始矩阵A的一个区间[left, right] x [top, bottom],将差分矩阵B的相应位置进行更新,即B[left][top] += val,B[right+1][top] -= val,B[left][bottom+1] -= val,B[right+1][bottom+1] += val。 3. 区间查询:对于原始矩阵A的一个区间[left, right] x [top, bottom],通过求和差分矩阵B的相应位置得到区间和,即sum = B[right][bottom] - B[left-1][bottom] - B[right][top-1] + B[left-1][top-1]。 二维差分可以广泛应用于各种算法问题,例如矩阵区间求和、矩阵区间更新等。它的时间复杂度较低,适用于处理大规模的数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值