由重复的单位矩阵 所组成的二维网格空间)
定義
給定一個矩陣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
;