Address
https://www.lydsy.com/JudgeOnline/problem.php?id=4456
Solution
考虑借鉴 K-D Tree 的思想,使用分治来做。
首先将询问离线。
当处理
x
x
坐标为 ,
y
y
坐标为 的子矩形内,对第
lq
l
q
到
rq
r
q
个询问操作(这些询问起点都在该子矩形内)时,我们考虑:
如果
ry−ly>rx−lx
r
y
−
l
y
>
r
x
−
l
x
,那么就令
mid=⌊ly+ry2⌋
m
i
d
=
⌊
l
y
+
r
y
2
⌋
。
对于所有的
i∈[lx,rx]
i
∈
[
l
x
,
r
x
]
,分别以
(i,mid)
(
i
,
m
i
d
)
为起点,跑单源最短路(注意,这是网格图,需要使用 Dijkstra ,并且跑最短路只能在
x∈[lx,rx]
x
∈
[
l
x
,
r
x
]
y∈[ly,ry]
y
∈
[
l
y
,
r
y
]
范围内的点跑,否则 TLE )。
以
(i,mid)
(
i
,
m
i
d
)
为起点跑最短路之后,我们就可以枚举一遍
[lq,rq]
[
l
q
,
r
q
]
内的所有询问。
设
dis[x,y]
d
i
s
[
x
,
y
]
为
(i,mid)
(
i
,
m
i
d
)
到
(x,y)
(
x
,
y
)
的最短路。
如果一个询问是求
(sx,sy)
(
s
x
,
s
y
)
到
(ex,ey)
(
e
x
,
e
y
)
的最短路,那么就用
dis[sx,sy]+dis[ex,ey]
d
i
s
[
s
x
,
s
y
]
+
d
i
s
[
e
x
,
e
y
]
更新该询问的答案。
可以发现,到这里我们对于所有
[lq,rq]
[
l
q
,
r
q
]
内的询问,
求出了经过了线段
lx≤x≤rx,y=mid
l
x
≤
x
≤
r
x
,
y
=
m
i
d
的最短路。
如果不经过这条线段,我们就递归下去,这时候要把
[lq,rq]
[
l
q
,
r
q
]
内的询问分组。
如果一个询问的起点和终点都在矩形
x∈[lx,rx],y∈[ly,mid−1]
x
∈
[
l
x
,
r
x
]
,
y
∈
[
l
y
,
m
i
d
−
1
]
内,则分到第一组。
如果一个询问的起点和终点都在矩形
x∈[lx,rx],y∈[mid+1,ry]
x
∈
[
l
x
,
r
x
]
,
y
∈
[
m
i
d
+
1
,
r
y
]
内,则分到第二组。
否则分到第三组。
然后对于第一组的询问,递归到子矩形
x∈[lx,rx],y∈[ly,mid−1]
x
∈
[
l
x
,
r
x
]
,
y
∈
[
l
y
,
m
i
d
−
1
]
。
对于第二组询问,递归到子矩形
x∈[lx,rx],y∈[mid+1,ry]
x
∈
[
l
x
,
r
x
]
,
y
∈
[
m
i
d
+
1
,
r
y
]
。
如果不满足
ry−ly>rx−lx
r
y
−
l
y
>
r
x
−
l
x
,那么就令
mid=⌊lx+rx2⌋
m
i
d
=
⌊
l
x
+
r
x
2
⌋
,
处理线段
x=mid,ly≤x≤ry
x
=
m
i
d
,
l
y
≤
x
≤
r
y
上的点到其他点的最短路,
向子矩形
x∈[lx,mid−1],y∈[ly,ry]
x
∈
[
l
x
,
m
i
d
−
1
]
,
y
∈
[
l
y
,
r
y
]
和
x∈[mid+1,rx],y∈[ly,ry]
x
∈
[
m
i
d
+
1
,
r
x
]
,
y
∈
[
l
y
,
r
y
]
递归下去。
复杂度分析:
一次分治会导致子矩形大小至少减少一半,故递归深度
O(lognm)
O
(
log
n
m
)
。
这样,一次询问最多被更新
O(lognm)
O
(
log
n
m
)
次,这一部分复杂度
O(qlognm)
O
(
q
log
n
m
)
。
我们分治时,由于我们选择了按长边切开,故分治时分割线段的长度应为矩形长和宽的较小值,由于长乘以宽不超过
nm
n
m
,故长度最长为
O(nm−−−√)
O
(
n
m
)
。
我们每次分治时选出了
O(nm−−−√)
O
(
n
m
)
个源点跑最短路。
规模为
S
S
的图跑 Dijkstra 复杂度 。
由主定理得复杂度
O(nmnm−−−√lognm+qlognm)
O
(
n
m
n
m
log
n
m
+
q
log
n
m
)
。
Code
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Edge(u) for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 1e5 + 5, M = N << 2, INF = 0x3f3f3f3f;
int n, m, q, ecnt, nxt[M], adj[N], go[M], val[M], dis[N], ans[N],
ox[N], oy[N], vis[N], siv[N], T;
struct cyx {
int sx, sy, ex, ey, id, res;
} qry[N], q1[N], q2[N], q3[N];
struct pyz {
int x, y;
friend inline bool operator < (const pyz &a, const pyz &b) {
return a.y > b.y;
}
};
priority_queue<pyz> pq;
inline int which(const int &i, const int &j) {
return (i - 1) * m + j;
}
void add_edge(int u, int v, int w) {
nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v; val[ecnt] = w;
nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u; val[ecnt] = w;
}
inline void dijk(const int &lx, const int &rx, const int &ly,
const int &ry, const int &lq, const int &rq, const int &S) {
int i, u;
while (!pq.empty()) pq.pop(); T++;
dis[S] = 0; siv[S] = T;
pq.push((pyz) {S, dis[S]});
while (!pq.empty()) {
pyz x = pq.top(); pq.pop();
if (vis[x.x] == T) continue;
int u = x.x; vis[u] = T;
Edge(u) {
int tx = ox[v], ty = oy[v];
if (tx < lx || tx > rx || ty < ly || ty > ry)
continue;
if (siv[v] < T) siv[v] = T, dis[v] = INF;
if (dis[u] + val[e] < dis[v])
dis[v] = dis[u] + val[e], pq.push((pyz) {v, dis[v]});
}
}
For (i, lq, rq) qry[i].res = min(qry[i].res,
dis[which(qry[i].sx, qry[i].sy)] + dis[which(qry[i].ex, qry[i].ey)]);
}
inline void solve(const int &lx, const int &rx, const int &ly,
const int &ry, const int &lq, const int &rq) {
if (lx > rx || ly > ry || lq > rq) return;
int i, t1 = 0, t2 = 0, t3 = 0;
if (ry - ly > rx - lx) {
int mid = ly + ry >> 1, tot = lq - 1;
For (i, lx, rx) dijk(lx, rx, ly, ry, lq, rq, which(i, mid));
For (i, lq, rq)
if (qry[i].sy < mid && qry[i].ey < mid)
q1[++t1] = qry[i];
else if (qry[i].sy > mid && qry[i].ey > mid)
q2[++t2] = qry[i];
else q3[++t3] = qry[i];
For (i, 1, t1) qry[++tot] = q1[i];
For (i, 1, t2) qry[++tot] = q2[i];
For (i, 1, t3) qry[++tot] = q3[i];
solve(lx, rx, ly, mid - 1, lq, lq + t1 - 1);
solve(lx, rx, mid + 1, ry, lq + t1, lq + t1 + t2 - 1);
}
else {
int mid = lx + rx >> 1, tot = lq - 1;
For (i, ly, ry) dijk(lx, rx, ly, ry, lq, rq, which(mid, i));
For (i, lq, rq)
if (qry[i].sx < mid && qry[i].ex < mid)
q1[++t1] = qry[i];
else if (qry[i].sx > mid && qry[i].ex > mid)
q2[++t2] = qry[i];
else q3[++t3] = qry[i];
For (i, 1, t1) qry[++tot] = q1[i];
For (i, 1, t2) qry[++tot] = q2[i];
For (i, 1, t3) qry[++tot] = q3[i];
solve(lx, mid - 1, ly, ry, lq, lq + t1 - 1);
solve(mid + 1, rx, ly, ry, lq + t1, lq + t1 + t2 - 1);
}
}
int main() {
int i, j, x, y, u, v;
n = read(); m = read();
For (i, 1, n) For (j, 1, m - 1)
x = read(), add_edge(which(i, j), which(i, j + 1), x);
For (i, 1, n - 1) For (j, 1, m)
x = read(), add_edge(which(i, j), which(i + 1, j), x);
For (i, 1, n) For (j, 1, m) u = which(i, j),
ox[u] = i, oy[u] = j;
q = read();
For (i, 1, q) qry[i].sx = read(), qry[i].sy = read(),
qry[i].ex = read(), qry[i].ey = read(), qry[i].id = i,
qry[i].res = INF;
solve(1, n, 1, m, 1, q);
For (i, 1, q) ans[qry[i].id] = qry[i].res;
For (i, 1, q) printf("%d\n", ans[i]);
return 0;
}