Luogu 3350 [ZJOI2016]旅行者
BZOJ 4456
听若干个大佬讲过$n$遍终于写掉了。
我把时限基本上跑满了2333……
分治 + 最短路。
首先我们去分治这个矩形格子,找到一条长边把它对半切,对切开的边上的每一个点跑一遍最短路然后更新所有答案,接下来把询问分成两类,一类是两个端点都在切开的中间线一侧的,另一类是在两侧的。对于在中轴线一侧的点,我们把它加到一侧去继续分治,而在中轴线两侧的点就不用继续分治了,因为接下来的中轴线一定不会对它造成影响。
分治边界是没有对应询问的情况和只剩下一个点但是仍然有询问的情况,这时候可以直接用$0$更新当前的答案。
不会算时间复杂度,这篇博客证明是$O(S\sqrt{S}logS)$的,其中$S$代表面积。
另外,听说$spfa$比$dijskra$跑得快……
Code:
#include <cstdio> #include <cstring> #include <queue> #include <iostream> using namespace std; typedef pair <int, int> pin; const int N = 2e4 + 5; const int M = 1e5 + 5; const int inf = 0x3f3f3f3f; int n, m, qn, tot = 0, head[N], dis[N], ans[M], verx[N], very[N]; bool vis[N]; priority_queue <pin> Q; struct Edge { int to, nxt, val; } e[N << 3]; inline void add(int from, int to, int val) { e[++tot].to = to; e[tot].val = val; e[tot].nxt = head[from]; head[from] = tot; } inline void addEdge(int x, int y, int val) { add(x, y, val), add(y, x, val); } struct Querys { int xa, xb, ya, yb, id; } q[M], tmp[M]; inline void read(int &X) { X = 0; char ch = 0; int op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline void chkMin(int &x, int y) { if(y < x) x = y; } inline int id(int x, int y) { return (x - 1) * m + y; } void dij(int xl, int xr, int yl, int yr, int stx, int sty) { for(int i = xl; i <= xr; i++) for(int j = yl; j <= yr; j++) dis[id(i, j)] = inf, vis[id(i, j)] = 0; dis[id(stx, sty)] = 0; Q.push(pin(0, id(stx, sty))); for(; !Q.empty(); ) { int x = Q.top().second; Q.pop(); if(vis[x]) continue; vis[x] = 1; for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(verx[y] >= xl && verx[y] <= xr && very[y] >= yl && very[y] <= yr) { if(dis[y] > dis[x] + e[i].val) { dis[y] = dis[x] + e[i].val; Q.push(pin(-dis[y], y)); } } } } } void solve(int xl, int xr, int yl, int yr, int ql, int qr) { if(ql > qr) return; if(xl == xr && yl == yr) { for(int i = ql; i <= qr; i++) chkMin(ans[q[i].id], 0); return; } if(xr - xl > yr - yl) { int mid = ((xl + xr) >> 1); for(int i = yl; i <= yr; i++) { dij(xl, xr, yl, yr, mid, i); for(int j = ql; j <= qr; j++) { int x = id(q[j].xa, q[j].ya), y = id(q[j].xb, q[j].yb); chkMin(ans[q[j].id], dis[x] + dis[y]); } } int l = ql - 1, r = qr + 1; for(int i = ql; i <= qr; i++) { if(q[i].xa <= mid && q[i].xb <= mid) tmp[++l] = q[i]; else if(q[i].xa > mid && q[i].xb > mid) tmp[--r] = q[i]; } for(int i = ql; i <= l; i++) q[i] = tmp[i]; for(int i = r; i <= qr; i++) q[i] = tmp[i]; solve(xl, mid, yl, yr, ql, l); solve(mid + 1, xr, yl, yr, r, qr); } else { int mid = ((yl + yr) >> 1); for(int i = xl; i <= xr; i++) { dij(xl, xr, yl, yr, i, mid); for(int j = ql; j <= qr; j++) { int x = id(q[j].xa, q[j].ya), y = id(q[j].xb, q[j].yb); chkMin(ans[q[j].id], dis[x] + dis[y]); } } int l = ql - 1, r = qr + 1; for(int i = ql; i <= qr; i++) { if(q[i].ya <= mid && q[i].yb <= mid) tmp[++l] = q[i]; else if(q[i].ya > mid && q[i].yb > mid) tmp[--r] = q[i]; } for(int i = ql; i <= l; i++) q[i] = tmp[i]; for(int i = r; i <= qr; i++) q[i] = tmp[i]; solve(xl, xr, yl, mid, ql, l); solve(xl, xr, mid + 1, yr, r, qr); } } int main() { // freopen("tourist3.in", "r", stdin); // freopen("my.out", "w", stdout); // freopen("Sample.txt", "r", stdin); read(n), read(m); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) verx[id(i, j)] = i, very[id(i, j)] = j; /* for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) printf("%d %d %d\n", id(i, j), verx[id(i, j)], very[id(i, j)]); */ for(int i = 1; i <= n; i++) for(int j = 1; j < m; j++) { int v; read(v); addEdge(id(i, j), id(i, j + 1), v); } for(int i = 1; i < n; i++) for(int j = 1; j <= m; j++) { int v; read(v); addEdge(id(i, j), id(i + 1, j), v); } read(qn); for(int i = 1; i <= qn; i++) { read(q[i].xa), read(q[i].ya), read(q[i].xb), read(q[i].yb); q[i].id = i; } memset(ans, 0x3f, sizeof(ans)); solve(1, n, 1, m, 1, qn); for(int i = 1; i <= qn; i++) printf("%d\n", ans[i]); return 0; }