题目描述
小Y来到了一个新的城市旅行。她发现了这个城市的布局是网格状的,也就是有 nn 条从东到西的道路和 mm 条从南到北的道路,这些道路两两相交形成 n×mn×m 个路口 (i,j)(1≤i≤n,1≤j≤m)(i,j)(1≤i≤n,1≤j≤m)。
她发现不同的道路路况不同,所以通过不同的路口需要不同的时间。通过调查发现,从路口 (i,j)(i,j) 到路口 (i,j+1)(i,j+1) 需要时间 ri,jri,j,从路口 (i,j)(i,j) 到路口 (i+1,j)(i+1,j) 需要时间 ci,jci,j。注意这里的道路是双向的,也就是从路口 (i,j+1)(i,j+1) 到路口 (i,j)(i,j) 需要时间同样是 ri,jri,j。
小Y有 qq 个询问,她想知道从路口 (x1,y1)(x1,y1) 到路口 (x2,y2)(x2,y2) 最少需要花多少时间。
输入格式
第一行包含 2 个正整数 n,mn,m ,表示城市的大小。
接下来 nn 行,每行包含 m−1m−1 个整数,第 ii 行第 jj 个正整数表示从一个路口到另一个路口的时间 ri,jri,j。
接下来 n−1n−1 行,每行包含 mm 个整数,第 ii 行第 jj 个正整数表示从一个路口到另一个路口的时间 ci,jci,j。
接下来一行,包含1个正整数 qq ,表示小Y的询问个数。
接下来 qq 行,每行包含4个正整数 x1,y1,x2,y2x1,y1,x2,y2,表示两个路口的位置。
输出格式
输出共 qq 行,每行包含一个整数表示从一个路口到另一个路口最少需要花的时间。
样例输入
2 2
2
3
6 4
2
1 1 2 2
1 2 2 1
样例输出
6
7
题解
按照网格分治,x轴和y轴哪边大做哪边。
取中间那条线,那么询问在左右两边(或上下两边)的点一定会经过中间这条线上的点。
那么枚举中间这条线上的点,dij求最短路后枚举询问取min。
如果有询问的两个点在同一边的,继续分治下去即可。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 20100
#define P(x,y) ((x-1)*m+y)
using namespace std;
int n,m,a[N][4],Q,f[N],ans[N*5];
int fx[4][2]={0,-1,-1,0,0,1,1,0};
struct node{
int q1,w1,q2,w2,z;
}q[N*5],ql[N*5],qr[N*5];
struct note{
int x,y,d;
friend bool operator < (note a,note b){return a.d>b.d;}
};
priority_queue<note> d;
int read()
{
char c=getchar();int x=0;
for(;c<'0'||c>'9';c=getchar());
for(;c>='0'&&c<='9';c=getchar()) x=x*10+c-48;
return x;
}
void dij(int xs,int ys,int l1,int r1,int l2,int r2)
{
priority_queue<note> d;
fo(i,l1,r1) fo(j,l2,r2) f[P(i,j)]=0xfffffff;
d.push((note){xs,ys,0});
f[P(xs,ys)]=0;
while(!d.empty())
{
note jy=d.top();
d.pop();
int p=P(jy.x,jy.y);
if(jy.d>f[p]) continue;
fo(i,0,3)
{
int x=jy.x+fx[i][0],y=jy.y+fx[i][1];
if(x<l1||x>r1||y<l2||y>r2) continue;
if(f[p]+a[p][i]<f[P(x,y)])
{
f[P(x,y)]=f[p]+a[p][i];
d.push((note){x,y,f[P(x,y)]});
}
}
}
}
void solve(int l1,int r1,int l2,int r2,int l,int r)
{
if(l>r) return;
if(r1-l1>r2-l2)
{
int mid=(l1+r1)>>1;
fo(i,l2,r2)
{
dij(mid,i,l1,r1,l2,r2);
fo(j,l,r) ans[q[j].z]=min(ans[q[j].z],f[P(q[j].q1,q[j].w1)]+f[P(q[j].q2,q[j].w2)]);
}
int tl=0,tr=0;
fo(i,l,r)
{
if(q[i].q1<=mid&&q[i].q2<=mid) ql[++tl]=q[i];
if(q[i].q1>mid&&q[i].q2>mid) qr[++tr]=q[i];
}
fo(i,1,tl) q[i+l-1]=ql[i];
fo(i,1,tr) q[r-tr+i]=qr[i];
if(l1==r1) return;
solve(l1,mid,l2,r2,l,l+tl-1);
solve(mid+1,r1,l2,r2,r-tr+1,r);
}
else
{
int mid=(l2+r2)>>1;
fo(i,l1,r1)
{
dij(i,mid,l1,r1,l2,r2);
fo(j,l,r) ans[q[j].z]=min(ans[q[j].z],f[P(q[j].q1,q[j].w1)]+f[P(q[j].q2,q[j].w2)]);
}
int tl=0,tr=0;
fo(i,l,r)
{
if(q[i].w1<=mid&&q[i].w2<=mid) ql[++tl]=q[i];
if(q[i].w1>mid&&q[i].w2>mid) qr[++tr]=q[i];
}
fo(i,1,tl) q[i+l-1]=ql[i];
fo(i,1,tr) q[r-tr+i]=qr[i];
if(l2==r2) return;
solve(l1,r1,l2,mid,l,l+tl-1);
solve(l1,r1,mid+1,r2,r-tr+1,r);
}
}
int main()
{
scanf("%d%d",&n,&m);
fo(i,1,n) fo(j,1,m-1) a[P(i,j)][2]=a[P(i,j+1)][0]=read();
fo(i,1,n-1) fo(j,1,m) a[P(i,j)][3]=a[P(i+1,j)][1]=read();
scanf("%d",&Q);
fo(i,1,Q) scanf("%d%d%d%d",&q[i].q1,&q[i].w1,&q[i].q2,&q[i].w2),q[i].z=i;
memset(ans,127,sizeof(ans));
solve(1,n,1,m,1,Q);
fo(i,1,Q) printf("%d\n",ans[i]);
}