算法 {由重复的单位矩阵 所组成的二维网格空间}

由重复的单位矩阵 所组成的二维网格空间)

定義

給定一個矩陣A[N][M] (稱之為單位矩形), 整个网格空间 是由无数个彼此彼此相邻的单位矩形组成的;

整个网格空间形如:
A...A
A...A

比如单位矩形大小为2*3, 如下:
e f e f
c d c d
a b a b
e f e f
c d c d
a b a b

还需要指定: 单位矩阵的左下角 在空间中的坐标, 空间原点 一定位于 单位矩阵的某一个元素上;

需求是: 在这样的空间中 给定左下角(lx,ly) - 右上角(rx,ry), 求这个区域里 所有元素的值之和;
. 比如以上图为例, 假如单位矩阵的左下角 就对应空间原点(即空间原点为a), 则区域(-1,-1)-(1,1)為表示 fef bab dcd這些元素;

性質

如果你直接去做, 即直接找(xl,yl)-(xr,yr)這個區域裡面 除了單位矩陣外了 所有的碎片, 如果你這樣做 其實是很複雜的, 很容易出錯…
要藉助前綴和的思想, 即我們令Sum(x,y)(x,y>=0)為 (0,0)-(x,y)這個區域裡面的答案 (這相對來說 要比上面這種直接計算的方式 要簡單的多), 即我们只关注第一象限, 然后通过二维前缀和 Sum(xr,yr) - Sum(xl-1,yr) - Sum(xr, yl-1) + Sum(xl-1, yl-1);

于是 关键在于 如何求Sum(x,y); 对应代码里的__PrefixRectangles这个函数, 他会把(0,0) - (x,y)这个矩形 划分为 若干个子矩形;

d e f d e f
a b c a b c
d e f d e f
a b c a b c
假如空间原点在`a`, 这个单位矩形是3*2;
__PrefixRectangles( (3,2)) == { (1,(2,1)), (1,(0,1)), (1,(2,0)), (1,(0,0))};

其实这个__PrefixRectangles 他是默认 空间原点就等于单位矩形的左下角, 因此外界需要做坐标转换;

@DELI;

涉及到負數, 也沒關係, 你將他往右上角移動((k*LenX, k*LenY)步數)到 第一象限 即可;

代码

//{ ___GridSpace_2D_RepeatRectangle_Area_
struct ___GridSpace_2D_RepeatRectangle_Area_{
using __TypeSum_ = long long;
using __TypePoint_ = std::pair<long long, long long>;
    __TypePoint_ __Size; // 二维网格空间 是有无数个`Size`大小的单位矩形 重复而成; (e.g. `Size=(4,2)` 表示单位矩阵长4高2);
    __TypePoint_ __Start; // 单位矩阵的左下角 位于网格空间的`Start`坐标;
    __TypeSum_ __AreaSum( __TypePoint_ _right){ // 对于*单个Size大小*的单位矩阵 获取其内部的`(0,0)-right`子区域的元素之和;
        ASSERTsystem_( _right.first>=0 && _right.first<__Size.first, _right, __Size);
        ASSERTsystem_( _right.second>=0 && _right.second<__Size.second, _right, __Size);
        __TypeSum_ ANS = 0;
        FOR_( i, 0, _right.first){
            FOR_( j, 0, _right.second){
                ANS += ?;
            }
        }
        return ANS;
    }

void Initialize( __TypePoint_ const& _size, __TypePoint_ const& _start){
    __Size = _size;  __Start = _start;
    __Start.first %= __Size.first;  if( __Start.first > 0){ __Start.first -= __Size.first;}
    __Start.second %= __Size.second;  if( __Start.second > 0){ __Start.second -= __Size.second;}
}
auto __PrefixRectangles( __TypePoint_ _a){ // 将`(0,0) - _a`这个区域 划分成若干个子矩形;
    ASSERTsystem_( _a.first>=0 && _a.second>=0);
    auto xCount = (_a.first + 1) / __Size.first, xRest = (_a.first + 1) % __Size.first;
    auto yCount = (_a.second + 1) / __Size.second, yRest = (_a.second + 1) % __Size.second;
    std::vector< std::pair< long long, __TypePoint_> > ANS; // {(子矩阵的个数, 子矩阵的大小)} (比如`(3,(2,1))`表示 有3个 `(0,0)-(2,1)`的单位矩形的子矩阵);
    if( xCount>0 && yCount>0){ ANS.push_back( {xCount * yCount, {__Size.first-1, __Size.second-1}});}
    if( xCount>0 && yRest>0){ ANS.push_back( {xCount, {__Size.first-1, yRest-1}});}
    if( yCount>0 && xRest>0){ ANS.push_back( {yCount, {xRest-1, __Size.second-1}});}
    if( xRest>0 && yRest>0){ ANS.push_back( {1, {xRest-1, yRest-1}});}
    return ANS;
};
__TypeSum_ Get_AreaSum( __TypePoint_ _left, __TypePoint_ _right){
    ASSERTsystem_( _left.first<=_right.first && _left.second<=_right.second, _left, _right);
    //>> 把坐标转换为: 单位矩阵的左下角位于*原点*的坐标系;
    _left.first -= __Start.first;  _left.second -= __Start.second;
    _right.first -= __Start.first;  _right.second -= __Start.second;

    if( _left.first<=0){
        auto t = (-_left.first)/__Size.first + 1;
        _left.first += t*__Size.first;  _right.first += t*__Size.first;
    }
    if( _left.second<=0){
        auto t = (-_left.second)/__Size.second + 1;
        _left.second += t*__Size.second;  _right.second += t*__Size.second;
    }
    __TypeSum_ ANS = 0;
    { // 容斥原理 (和二维前缀和一样 获取`[left-right]`这个区域)
        auto ret = __PrefixRectangles( _right);
        for( auto & i : ret){ ANS += (i.first * __AreaSum( i.second));}

        ret = __PrefixRectangles( {_left.first-1, _right.second});
        for( auto & i : ret){ ANS -= (i.first * __AreaSum( i.second));}

        ret = __PrefixRectangles( {_right.first, _left.second-1});
        for( auto & i : ret){ ANS -= (i.first * __AreaSum( i.second));}

        ret = __PrefixRectangles( {_left.first-1, _left.second-1});
        for( auto & i : ret){ ANS += (i.first * __AreaSum( i.second));}
    }
    return ANS;
}
};
//} ___GridSpace_2D_RepeatRectangle_Area_

例题

@LINK: https://atcoder.jp/contests/abc354/tasks/abc354_d;
单位矩阵是4*2, 且单位矩阵的左下角 位于空间的坐标是(-2,0);
这个单位矩阵的值为:

1 0 1 2
0 1 2 1

注意, 题目给定的(lx,ly)-(rx,ry), 其实他对应的真正的坐标是(lx,ly)-(rx-1,ry-1);

@DELI;

@LINK: https://atcoder.jp/contests/abc331/tasks/abc331_d;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值