题解 P3350 [ZJOI2016]旅行者
洛谷题面传送门
肿么没有人证明复杂度,那我来证一个。
考虑分治,每次像猫树那样处理一个分治区间
如果每次都劈
接下来考虑复杂度,注意到一轮更新的复杂度是
设
显然
其中
const int MAXV = 2e4;
const int MAXQ = 1e5;
const int INF = 0x3f3f3f3f;
int n, m, qu, res[MAXQ + 5]; pii rid[MAXV + 5];
struct qry {int x1, y1, x2, y2;} q[MAXQ + 5];
int hd[MAXV + 5], to[MAXV * 4 + 5], nxt[MAXV * 4 + 5], val[MAXV * 4 + 5], ec = 0;
int getid(int x, int y) {return (x - 1) * m + y;}
void adde(int u, int v, int w) {to[++ec] = v; val[ec] = w; nxt[ec] = hd[u]; hd[u] = ec;}
int dis[MAXV + 5];
void dijkstra(int l1, int r1, int l2, int r2, int X, int Y) {
if (dis[getid(X, Y)] != INF) {
for (int i = l1; i <= r1; i++) for (int j = l2; j <= r2; j++)
if (i != X || j != Y) dis[getid(i, j)] += dis[getid(X, Y)];
}
dis[getid(X, Y)] = 0;
priority_queue<pii, vector<pii>, greater<pii> > q;
q.push(mp(0, getid(X, Y)));
while (!q.empty()) {
pii p = q.top(); q.pop(); int x = p.se;
for (int e = hd[x]; e; e = nxt[e]) {
int y = to[e], z = val[e];
if (l1 <= rid[y].fi && rid[y].fi <= r1 && l2 <= rid[y].se && rid[y].se <= r2) {
if (dis[y] > dis[x] + z) dis[y] = dis[x] + z, q.push(mp(dis[y], y));
}
}
}
}
void solve(int l1, int r1, int l2, int r2, vector<int> cd) {
if (l1 == r1 && l2 == r2) {
for (int id : cd) res[id] = 0;
return;
}
if (r1 - l1 + 1 > r2 - l2 + 1) {
int mid = l1 + r1 >> 1;
for (int i = l1; i <= r1; i++) for (int j = l2; j <= r2; j++) dis[getid(i, j)] = INF;
for (int i = l2; i <= r2; i++) {
dijkstra(l1, r1, l2, r2, mid, i);
for (int id : cd) chkmin(res[id], dis[getid(q[id].x1, q[id].y1)] + dis[getid(q[id].x2, q[id].y2)]);
}
vector<int> L, R;
for (int id : cd) {
if (max(q[id].x1, q[id].x2) <= mid) L.pb(id);
if (min(q[id].x1, q[id].x2) > mid) R.pb(id);
}
solve(l1, mid, l2, r2, L); solve(mid + 1, r1, l2, r2, R);
} else {
int mid = l2 + r2 >> 1;
for (int i = l1; i <= r1; i++) for (int j = l2; j <= r2; j++) dis[getid(i, j)] = INF;
for (int i = l1; i <= r1; i++) {
dijkstra(l1, r1, l2, r2, i, mid);
for (int id : cd) chkmin(res[id], dis[getid(q[id].x1, q[id].y1)] + dis[getid(q[id].x2, q[id].y2)]);
}
vector<int> L, R;
for (int id : cd) {
if (max(q[id].y1, q[id].y2) <= mid) L.pb(id);
if (min(q[id].y1, q[id].y2) > mid) R.pb(id);
}
solve(l1, r1, l2, mid, L); solve(l1, r1, mid + 1, r2, R);
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++)
rid[getid(i, j)] = mp(i, j);
for (int i = 1; i <= n; i++) for (int j = 1; j < m; j++) {
int x; scanf("%d", &x);
adde(getid(i, j), getid(i, j + 1), x);
adde(getid(i, j + 1), getid(i, j), x);
}
for (int i = 1; i < n; i++) for (int j = 1; j <= m; j++) {
int x; scanf("%d", &x);
adde(getid(i, j), getid(i + 1, j), x);
adde(getid(i + 1, j), getid(i, j), x);
}
scanf("%d", &qu); memset(res, 63, sizeof(res));
for (int i = 1; i <= qu; i++) scanf("%d%d%d%d", &q[i].x1, &q[i].y1, &q[i].x2, &q[i].y2);
vector<int> all; for (int i = 1; i <= qu; i++) all.pb(i);
solve(1, n, 1, m, all);
for (int i = 1; i <= qu; i++) printf("%d\n", res[i]);
return 0;
}