Luogu P3397 地毯

· · 个人记录

Luogu P3397 地毯

一道二维差分的板子题。

因为二维差分比起一维的来说并不那么直观,所以这里主要讲一下二维差分的实现思路。

首先,记二维前缀和为 S_{a_{x,y}}= \sum^{x}_{i=1} \sum^{y}_{j=1} a_{i,j};二维差分为 D_{x,y}

显然,S_{a_{x,y}} 满足如下递推式:

S_{a_{x,y}}=S_{a_{x,y-1}}+S_{a_{x-1,y}}-S_{a_{x-1,y-1}}+a_{x,y}

这里,我们要用到一个性质:差分的前缀和为原数,即 S_{D_{x,y}}=a_{x,y}

则代入二维前缀和的递推式,可以得到二维差分满足的关系式:

a_{x,y}=a_{x,y-1}+a_{x-1,y}-a_{x-1,y-1}+D_{x,y}

变形一下得到二维差分的计算式:

D_{x,y}=a_{x,y}-a_{x,y-1}-a_{x-1,y}+a_{x-1,y-1}

最后我们再来考虑如何实现题目中,修改一个左上角是 (x_1,y_1),右下角是 (x_2,y_2) 的矩形区间。

类比前缀和的计算,不难得到:

即可。

代码实现如下:

#include<bits/stdc++.h>
#define N 1010

int n,m;
int map[N][N],d[N][N];

struct node {
    int x1,y1,x2,y2;
};

node a[N];

namespace WalkerV {
    void Read() {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++) {
            scanf("%d%d%d%d",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2);
        }
        return;
    }

    void Solve() {
        for(int i=1;i<=m;i++) {
            d[a[i].x1][a[i].y1]++;
            d[a[i].x2+1][a[i].y1]--;
            d[a[i].x1][a[i].y2+1]--;
            d[a[i].x2+1][a[i].y2+1]++;
        }
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=n;j++) {
                map[i][j]=map[i][j-1]+map[i-1][j]-map[i-1][j-1]+d[i][j];
            }
        }
        return;
    }

    void Print() {
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=n;j++) {
                printf("%d%c",map[i][j],j==n?'\n':' ');
            }
        }
        return;
    }
}

int main()
{
    WalkerV::Read();
    WalkerV::Solve();
    WalkerV::Print();
    return 0;
}