4456: [Zjoi2016]旅行者
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 651 Solved: 375
[Submit][Status][Discuss]
Description
小Y来到了一个新的城市旅行。她发现了这个城市的布局是网格状的,也就是有n条从东到西的道路和m条从南到北
的道路,这些道路两两相交形成n×m个路口 (i,j)(1≤i≤n,1≤j≤m)。她发现不同的道路路况不同,所以通过不
同的路口需要不同的时间。通过调查发现,从路口(i,j)到路口(i,j+1)需要时间 r(i,j),从路口(i,j)到路口(i+1
,j)需要时间c(i,j)。注意这里的道路是双向的。小Y有q个询问,她想知道从路口(x1,y1)到路口(x2,y2)最少需要
花多少时间。
Input
第一行包含 2 个正整数n,m,表示城市的大小。
接下来n行,每行包含m?1个整数,第i行第j个正整数表示从一个路口到另一个路口的时间r(i,j)。
接下来n?1行,每行包含m个整数,第i行第j个正整数表示从一个路口到另一个路口的时间c(i,j)。
接下来一行,包含1个正整数q,表示小Y的询问个数。
接下来q行,每行包含4个正整数 x1,y1,x2,y2,表示两个路口的位置。
Output
输出共q行,每行包含一个整数表示从一个路口到另一个路口最少需要花的时间。
Sample Input
2 2
2
3
6 4
2
1 1 2 2
1 2 2 1
2
3
6 4
2
1 1 2 2
1 2 2 1
Sample Output
6
7
7
n*m ≤ 2×10^4
q ≤ 10^5
相邻路口时间不超过10^4
q ≤ 10^5
相邻路口时间不超过10^4
分析:idea很妙的一道题.
和棋盘迷宫这道题的想法差不多. 多组询问,要么一组一组地快速处理,要么像整体二分一样一下子全部处理完. 这道题采用平面内分治的方法来回答所有询问.
画一条中线. 询问的两个点都在中线同一侧的询问,会在以后的递归中处理. 考虑两个点在中线异侧的询问. 这两个点之间的路径一定经过会经过中线上的两个点(这两个点可能重合).那么以中线上的每一个点为源点做dijkstra,用到达两个点的路径长度和去更新答案即可.
那么中线是哪一条线呢?(到底是分治x还是分治y呢?)
显然分治越大的越好,因为要枚举中线上的点. 如果x大,就分治x,y大就分治y.
注意,这道题卡常数!几个小小的优化:
1.dijkstra的vis数组用时间戳优化。
2.如果dijkstra松弛操作的点在当前分治层的边界之外了,就不要去松弛它.
3.数组开小点,memset就会快点.
再来看一看这道题和棋盘迷宫有什么相似点:
1.平面图(边与边的交点只会在顶点上,这样可以分治)
2.多组询问,支持离线.
3.询问的是路径.
通用的解法是分治中线,求这条中线上的所有点到询问的点对之间的答案.
#include <cstdio> #include <queue> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 500010; int n,m,ans[maxn],Q,d[20010],head[maxn],to[maxn],nextt[maxn],tot = 1,w[maxn],idx[maxn],idy[maxn],vis[maxn],Tim; struct node { int x3,y3,x4,y4,x,y,id; } e[maxn],p[maxn]; struct node2 { int len,x; bool operator < (const node2 &b) const { return len > b.len; } }; int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void add(int x,int y,int z) { w[tot] = z; to[tot] = y; nextt[tot] = head[x]; head[x] = tot++; } int id(int x,int y) { return (x - 1) * m + y; } void dijkstra(int S,int x3,int y3,int x4,int y4) { priority_queue <node2> q; Tim++; node2 temp; temp.x = S; temp.len = 0; q.push(temp); memset(d,127/3,sizeof(d)); d[S] = 0; while (!q.empty()) { node2 u = q.top(); q.pop(); if (vis[u.x] == Tim) continue; vis[u.x] = Tim; for (int i = head[u.x]; i; i = nextt[i]) { int v = to[i]; if (idx[v] < x3 || idx[v] > x4 || idy[v] < y3 || idy[v] > y4) continue; if (d[v] > d[u.x] + w[i]) { d[v] = d[u.x] + w[i]; node2 temp; temp.x = v; temp.len = d[v]; q.push(temp); } } } } void solve(int x3,int y3,int x4,int y4,int ql,int qr) { if (x3 > x4 || y3 > y4 || ql > qr) return; if (x4 - x3 <= y4 - y3) { int mid = (y3 + y4) >> 1; int L = ql - 1,R = qr + 1; for (int i = ql; i <= qr; i++) { if (e[i].y3 < mid && e[i].y4 < mid) p[++L] = e[i]; else if (e[i].y3 > mid && e[i].y4 > mid) p[--R] = e[i]; } for (int i = x3; i <= x4; i++) { dijkstra(id(i,mid),x3,y3,x4,y4); for (int j = ql; j <= qr; j++) ans[e[j].id] = min(ans[e[j].id],d[e[j].x] + d[e[j].y]); } for (int i = ql; i <= L; i++) e[i] = p[i]; for (int i = R; i <= qr; i++) e[i] = p[i]; solve(x3,y3,x4,mid - 1,ql,L); solve(x3,mid + 1,x4,y4,R,qr); } else { int mid = (x3 + x4) >> 1; int L = ql - 1,R = qr + 1; for (int i = ql; i <= qr; i++) { if (e[i].x3 < mid && e[i].x4 < mid) p[++L] = e[i]; if (e[i].x3 > mid && e[i].x4 > mid) p[--R] = e[i]; } for (int i = y3; i <= y4; i++) { dijkstra(id(mid,i),x3,y3,x4,y4); for (int j = ql; j <= qr; j++) ans[e[j].id] = min(ans[e[j].id],d[e[j].x] + d[e[j].y]); } for (int i = ql; i <= L; i++) e[i] = p[i]; for (int i = R; i <= qr; i++) e[i] = p[i]; solve(x3,y3,mid - 1,y4,ql,L); solve(mid + 1,y3,x4,y4,R,qr); } } int main() { memset(ans,127/3,sizeof(ans)); n = read(),m = read(); for (int i = 1; i <= n; i++) for (int j = 1; j < m; j++) { int t; t = read(); add(id(i,j),id(i,j + 1),t); add(id(i,j + 1),id(i,j),t); } for (int i = 1; i < n; i++) for (int j = 1; j <= m; j++) { int t; t = read(); add(id(i,j),id(i + 1,j),t); add(id(i + 1,j),id(i,j),t); } for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { int t = id(i,j); idx[t] = i; idy[t] = j; } Q = read(); for (int i = 1; i <= Q; i++) { e[i].x3 = read(),e[i].y3 = read(),e[i].x4 = read(),e[i].y4 = read(); e[i].x = id(e[i].x3,e[i].y3); e[i].y = id(e[i].x4,e[i].y4); e[i].id = i; } solve(1,1,n,m,1,Q); for (int i = 1; i <= Q; i++) printf("%d\n",ans[i]); return 0; }