problem
luogu-P3350
solution
据说,网格图最短路用分治是一个人人皆知的套路。对不起我不是人
类比整体二分的算法流程。
考虑在一个 (xl,yl)−(yl,yr)(xl,yl)-(yl,yr)(xl,yl)−(yl,yr) 矩阵内处理 [ql,qr][ql,qr][ql,qr] 的询问。
以矩阵的中界线 mid\text{mid}mid 将矩阵划成两半,显然哪一维更长划哪一维。
以中界线上的每一个点为起点跑一遍最短路,然后更新所有需要跨过这条线的询问的答案,相当于是强制路线必须经过该点。
如果不需要跨过这条线,即询问的起终点均在线的某一侧,就分成 l,rl,rl,r 两个部分,继续分治下去。
时间复杂度好像都说是 O(NNlogN)O(N\sqrt{N}\log N)O(NNlogN)。
具体见代码即可明白。
code
#include <bits/stdc++.h>
using namespace std;
#define maxn 20005
#define maxq 100005
#define Pair pair < int, int >
struct node { int u, v, id; }q[maxq], l[maxq], r[maxq];
vector < Pair > G[maxn];
priority_queue < Pair, vector < Pair >, greater < Pair > > que;
int x[maxn], y[maxn], dis[maxn], ans[maxq];
int n, m, Q;int id( int i, int j ) { return (i - 1) * m + j; }void dijkstra( int s, int xl, int xr, int yl, int yr ) {for( int i = xl;i <= xr;i ++ )for( int j = yl;j <= yr;j ++ )dis[id(i, j)] = 0x7f7f7f7f;que.push( make_pair( dis[s] = 0, s ) );while( ! que.empty() ) {int u = que.top().second, w = que.top().first;que.pop();if( dis[u] ^ w ) continue;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i].first; w = G[u][i].second;if( x[v] < xl or x[v] > xr or y[v] < yl or y[v] > yr ) continue;if( dis[v] > dis[u] + w )que.push( make_pair( dis[v] = dis[u] + w, v ) );}}
}void solve( int ql, int qr, int xl, int xr, int yl, int yr ) {if( ql > qr or xl > xr or yl > yr ) return;int cntl = 0, cntr = 0;if( xr - xl >= yr - yl ) {int mid = xr + xl >> 1;for( int i = yl;i <= yr;i ++ ) {dijkstra( id(mid, i), xl, xr, yl, yr );for( int j = ql;j <= qr;j ++ )ans[q[j].id] = min( ans[q[j].id], dis[q[j].u] + dis[q[j].v] );}for( int i = ql;i <= qr;i ++ ) {if( x[q[i].u] < mid and x[q[i].v] < mid ) l[++ cntl] = q[i];if( x[q[i].u] > mid and x[q[i].v] > mid ) r[++ cntr] = q[i];}for( int i = 1;i <= cntl;i ++ ) q[ql + i - 1] = l[i];for( int i = 1;i <= cntr;i ++ ) q[ql + cntl + i - 1] = r[i];solve( ql, ql + cntl - 1, xl, mid - 1, yl, yr );solve( ql + cntl, ql + cntl + cntr - 1, mid + 1, xr, yl, yr );}else {int mid = yr + yl >> 1;for( int i = xl;i <= xr;i ++ ) {dijkstra( id(i, mid), xl, xr, yl, yr );for( int j = ql;j <= qr;j ++ )ans[q[j].id] = min( ans[q[j].id], dis[q[j].u] + dis[q[j].v] );}for( int i = ql;i <= qr;i ++ ) {if( y[q[i].u] < mid and y[q[i].v] < mid ) l[++ cntl] = q[i];if( y[q[i].u] > mid and y[q[i].v] > mid ) r[++ cntr] = q[i];}for( int i = 1;i <= cntl;i ++ ) q[ql + i - 1] = l[i];for( int i = 1;i <= cntr;i ++ ) q[ql + cntl + i - 1] = r[i];solve( ql, ql + cntl - 1, xl, xr, yl, mid - 1 );solve( ql + cntl, ql + cntl + cntr - 1, xl, xr, mid + 1, yr );}
}int main() {scanf( "%d %d", &n, &m );for( int i = 1;i <= n;i ++ )for( int j = 1, x;j < m;j ++ ) {scanf( "%d", &x );G[id(i, j)].push_back( make_pair( id(i, j + 1), x ) );G[id(i, j + 1)].push_back( make_pair( id(i, j), x ) );}for( int i = 1;i < n;i ++ )for( int j = 1, x;j <= m;j ++ ) {scanf( "%d", &x );G[id(i, j)].push_back( make_pair( id(i + 1, j), x ) );G[id(i + 1, j)].push_back( make_pair( id(i, j), x ) );}for( int i = 1;i <= n;i ++ )for( int j = 1;j <= m;j ++ )x[id(i, j)] = i, y[id(i, j)] = j;scanf( "%d", &Q );for( int i = 1, a1, b1, a2, b2;i <= Q;i ++ ) {scanf( "%d %d %d %d", &a1, &b1, &a2, &b2 );q[i] = (node){ id(a1, b1), id(a2, b2) }, q[i].id = i;}memset( ans, 0x7f, sizeof( ans ) );solve( 1, Q, 1, n, 1, m );for( int i = 1;i <= Q;i ++ ) printf( "%d\n", ans[i] );return 0;
}