关于二维前缀和,我们先从一道例题引入
例题引入
题目链接
解题思路
嗯.看起来就是道广搜。
只要我们考虑怎么处理障碍,我们就可以很简单地套广搜模板上去。
设有障碍为1,无障碍为0,我们如何处理障碍?
我们可以很直观地想到,只要我要走的一条路
(
x
s
,
y
s
)
t
o
(
x
e
,
y
e
)
(x_s,y_s)\ to\ (x_e,y_e)
(xs,ys) to (xe,ye)上没有一个0,我们就可以往下走。我们可以想到使用前缀和来维护区间问题 。
先放代码自行体会,等下再来说二维前缀和。
详细代码
#define USEFASTERREAD 1
#include<cstdio>
typedef long long ll;
#define DEBUG printf("Passing Line %d in [%s]", __LINE__, __func__)
#define rg register
#define Rep(i, s, t) for(rg int i = s; i <= t; i++)
#define Repd(i, t, s) for(rg int i = t; i >= s; i--)
#if USEFASTERREAD
char In[1 << 20], *ss = In, *tt = In;
#define getchar() (ss == tt && (tt = (ss = In) + fread(In, 1, 1<<20, stdin), tt == ss) ? EOF : *ss++)
#endif
struct IO {
template<typename T> inline IO r(T& x)const {
x = 0; T f = 1; char ch = getchar();
for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + int(ch - '0');
x *= f; return *this;
}
inline IO r(char& x)const {
x = getchar();
while(x != '$' && x != '#' && x !='X' && x != '.') x = getchar();
return *this;
}
template<typename T> inline IO w(T x)const {
if(x < 0) {putchar('-'); x = -x;}
if(x >= 10) w(x / 10);
putchar('0' + x % 10);
return *this;
}
inline IO l()const {putchar('\n'); return *this;}
inline IO s()const {putchar(' '); return *this;}
template<typename T> inline IO wl(T x)const {w(x).l();return *this;}
template<typename T> inline IO ws(T x)const {w(x).s();return *this;}
}io;
template<typename T> inline T Max(const T& x, const T& y) {return x < y ? y : x;}
template<typename T> inline T Min(const T& x, const T& y) {return x < y ? x : y;}
template<typename T> inline void Swap(T& x, T& y) {T t = x; x = y; y = t;}
template<typename T> inline T Abs(const T& x) {return x > 0 ? x : -x;}
#include<queue>
#include<cstring>
using namespace std;
struct Node {
int x, y, st;
Node(int x = 0, int y = 0, int st = 0) : x(x), y(y), st(st) {}
};
const int MAXN = 1005;
const int INF = 0x3f3f3f3f;
const int
dx[4] = {1, -1, 0, 0},
dy[4] = {0, 0, 1, -1};
char A[MAXN][MAXN];
int sum[MAXN][MAXN];
bool vist[MAXN][MAXN];
int tx, ty;
int n, m;
queue<Node> q;
bool check(int x, int y) {
return x >= 1 && x <= n && y >= 1 && y <= m;
}
int main() {
io.r(n).r(m);
Rep(i, 1, n)
Rep(j, 1, m) {
io.r(A[i][j]);
if(A[i][j] == '#') tx = i, ty = j;
}
Rep(i, 1, n)
Rep(j, 1, m) {
if(i == 1 && j == 1) sum[i][j] = 0;
else sum[i][j] = sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1] + (A[i][j] != '$' && A[i][j] != '#');
}
q.push(Node(1, 1, 0));
while(!q.empty()) {
Node now = q.front(); q.pop();
int x = now.x, y = now.y, st = now.st;
if(vist[x][y]) continue;
vist[x][y] = 1;
if(x == tx && y == ty) {
io.wl(st);
return 0;
}
for(rg int i = 0; i < 4; i++) {
for(rg int k = 0; (1 << k) <= n || (1 << k) <= m; k++) {
int nx = x + dx[i] * (1 << k), ny = y + dy[i] * (1 << k);
if(check(nx, ny) && Abs(sum[nx][ny] - sum[nx][y - 1] - sum[x - 1][ny] + sum[x - 1][y - 1] == 0) && !vist[nx][ny])
q.push(Node(nx, ny, st + 1));
}
}
}
return 0;
}
图文理解
递推求二维前缀和
我们的二维前缀和其实是维护从左上角(一般是
(
1
,
1
)
(1,1)
(1,1)开始,这样可以不用考虑边界)到当前的右下角
(
n
,
m
)
(n,m)
(n,m)的一个矩阵的元素的和
s
u
m
n
m
sum_{nm}
sumnm。
即
s
u
m
n
m
=
∑
i
=
1
n
∑
j
=
1
m
A
i
j
sum_{nm}=\sum_{i=1}^n\sum_{j=1}^mA_{ij}
sumnm=i=1∑nj=1∑mAij
我们欲求
这块矩阵的元素和
考虑
s
u
m
n
m
sum_{nm}
sumnm如何通过递推得到
我们有
s
u
m
n
m
=
s
u
m
(
n
−
1
)
m
+
s
u
m
n
(
m
−
1
)
−
s
u
m
(
n
−
1
)
(
m
−
1
)
+
a
n
m
sum_{nm}=sum_{(n-1)m}+sum_{n(m-1)}-sum_{(n-1)(m-1)}+a_{nm}
sumnm=sum(n−1)m+sumn(m−1)−sum(n−1)(m−1)+anm
为何如此?
我们从图形上理解。
我们把二维前缀和理解成矩阵的“面积”
故
S
蓝
色
矩
阵
=
S
黄
色
矩
阵
+
S
绿
色
矩
阵
−
S
黑
色
矩
阵
+
S
橙
色
矩
阵
S_{蓝色矩阵}=S_{黄色矩阵}+S_{绿色矩阵}-S_{黑色矩阵}+S_{橙色矩阵}
S蓝色矩阵=S黄色矩阵+S绿色矩阵−S黑色矩阵+S橙色矩阵
翻译过来就是
s
u
m
n
m
=
s
u
m
(
n
−
1
)
m
+
s
u
m
n
(
m
−
1
)
−
s
u
m
(
n
−
1
)
(
m
−
1
)
+
a
n
m
sum_{nm}=sum_{(n-1)m}+sum_{n(m-1)}-sum_{(n-1)(m-1)}+a_{nm}
sumnm=sum(n−1)m+sumn(m−1)−sum(n−1)(m−1)+anm
有个求二维前缀和的口诀:上加左,减左上,加自己
O(1)求矩阵元素和
为什么我们要使用二维前缀和?主要还是为了可以
O
(
1
)
O(1)
O(1)算出给定的一块矩阵的元素和
设我们求一块左上为
(
x
,
y
)
(x,y)
(x,y),右下为
(
n
,
m
)
(n,m)
(n,m)的矩阵的元素和。
同样,我们从图形的角度来理解
我们要求的是
S
红
色
矩
形
S_{红色矩形}
S红色矩形
根据图形,易得:
S
红
色
矩
形
=
S
蓝
色
矩
形
−
S
黄
色
矩
形
−
S
绿
色
矩
形
+
S
黑
色
矩
形
S_{红色矩形}=S_{蓝色矩形}-S_{黄色矩形}-S_{绿色矩形}+S_{黑色矩形}
S红色矩形=S蓝色矩形−S黄色矩形−S绿色矩形+S黑色矩形
翻译一下,就是:
a
n
s
=
s
u
m
n
m
−
s
u
m
(
x
−
1
)
m
−
s
u
m
n
(
y
−
1
)
+
s
u
m
(
x
−
1
)
(
y
−
1
)
ans=sum_{nm}-sum_{(x-1)m}-sum_{n(y-1)}+sum_{(x-1)(y-1)}
ans=sumnm−sum(x−1)m−sumn(y−1)+sum(x−1)(y−1)
忘记了的话,现场推就好了,画个图。