题意:给一个矩阵,个询问,每次询问两点最短路。
考虑最暴力的做法,每次直接暴力跑最短路,然后计算答案。这样的不足之处显然在于最短路上面,那么如何优化这个过程?
考虑计算所有的答案。
也就是分治,每次选择一条边,将它切开,考虑它对哪些询问有影响。
- 如果一个询问的两个点必须经过这条线,那么最优答案肯定在这条线上,对这条线的每个点做一遍最短路。然后去更新这些询问的答案。
- 如果一个点的两端点不需要经过这条线,最优答案也是有可能在这条线上的,还是对它们更新答案,然后继续分治下去。
考虑到时间问题,每次应该切开最长边,这样可以做最少次数的最短路。
#include <bits/stdc++.h>
#define regi register int
int n,m;
int totq;
int head[1000001],tot;
int dist[1000001];
std::priority_queue<std::pair<int,int>,std::vector<std::pair<int,int> >,std::greater<std::pair<int,int> > >que;
struct edge{
int to;
int nxt;
int w;
}e[1000001];
struct Question{
int lx;
int ly;
int rx;
int ry;
int id;
}q[1000001],tmp[1000001];
int ans[1000001];
inline int read(){
int r=0,w=0,c;
for(;!isdigit(c=getchar());r=c);
for(w=c^48;isdigit(c=getchar());w=w*10+(c^48));
return r^45?w:-w;
}
int get_number(int x,int y){
return (x-1)*m+y;
}
std::pair<int,int> return_number(int x){
return (x%m)?std::make_pair(x/m+1,x%m):std::make_pair(x/m,m);
}
int ok(int x,int y,int lx,int ly,int rx,int ry){
return lx<=x&&x<=rx&&ly<=y&&y<=ry;
}
void add(int x,int y,int z){
e[++tot]={y,head[x],z};
head[x]=tot;
e[++tot]={x,head[y],z};
head[y]=tot;
}
void dijkstra(int lx,int ly,int rx,int ry,int x){
for(regi i=lx;i<=rx;++i)
for(regi j=ly;j<=ry;++j){
dist[get_number(i,j)]=0x3f3f3f3f;
}
dist[x]=0;
que.push({dist[x],x});
while(!que.empty()){
int X=que.top().second,Y=que.top().first;
que.pop();
for(regi i=head[X];i;i=e[i].nxt){
regi y=e[i].to;
std::pair<int,int>pp=return_number(y);
if(pp.first<lx||pp.first>rx||pp.second<ly||pp.second>ry)
continue;
if(dist[y]>dist[X]+e[i].w){
dist[y]=dist[X]+e[i].w;
que.push({dist[y],y});
}
}
}
}
void solve(int lx,int rx,int ly,int ry,int l,int r){
if(lx==rx&&ly==ry)
return;
if(rx-lx>=ry-ly){
int mid=lx+rx>>1;
for(regi i=ly;i<=ry;++i){
dijkstra(lx,ly,rx,ry,get_number(mid,i));
for(regi j=l;j<=r;++j)
ans[q[j].id]=std::min(ans[q[j].id],dist[get_number(q[j].lx,q[j].ly)]+dist[get_number(q[j].rx,q[j].ry)]);
}
int nl=l-1,nr=r+1;
for(regi j=l;j<=r;++j)
if(ok(q[j].lx,q[j].ly,lx,ly,mid,ry)&&ok(q[j].rx,q[j].ry,lx,ly,mid,ry))
tmp[++nl]=q[j];
else
if(ok(q[j].lx,q[j].ly,mid+1,ly,rx,ry)&&ok(q[j].rx,q[j].ry,mid+1,ly,rx,ry))
tmp[--nr]=q[j];
for(regi j=l;j<=nl;++j)
q[j]=tmp[j];
for(regi j=r;j>=nr;--j)
q[j]=tmp[j];
solve(lx,mid,ly,ry,l,nl);
solve(mid+1,rx,ly,ry,nr,r);
}
else{
int mid=ly+ry>>1;
for(regi i=lx;i<=rx;++i){
dijkstra(lx,ly,rx,ry,get_number(i,mid));
for(regi j=l;j<=r;++j)
ans[q[j].id]=std::min(ans[q[j].id],dist[get_number(q[j].lx,q[j].ly)]+dist[get_number(q[j].rx,q[j].ry)]);
}
int nl=l-1,nr=r+1;
for(regi j=l;j<=r;++j)
if(ok(q[j].lx,q[j].ly,lx,ly,rx,mid)&&ok(q[j].rx,q[j].ry,lx,ly,rx,mid))
tmp[++nl]=q[j];
else
if(ok(q[j].lx,q[j].ly,lx,mid+1,rx,ry)&&ok(q[j].rx,q[j].ry,lx,mid+1,rx,ry))
tmp[--nr]=q[j];
for(regi j=l;j<=nl;++j)
q[j]=tmp[j];
for(regi j=r;j>=nr;--j)
q[j]=tmp[j];
solve(lx,rx,ly,mid,l,nl);
solve(lx,rx,mid+1,ry,nr,r);
}
}
main(){
n=read();
m=read();
for(regi i=1;i<=n;++i)
for(regi j=1;j<m;++j){
regi x=read();
add(get_number(i,j),get_number(i,j+1),x);
}
for(regi i=1;i<n;++i)
for(regi j=1;j<=m;++j){
regi x=read();
add(get_number(i,j),get_number(i+1,j),x);
}
totq=read();
for(regi i=1;i<=totq;++i){
q[i].lx=read();
q[i].ly=read();
q[i].rx=read();
q[i].ry=read();
q[i].id=i;
}
for(regi i=1;i<=totq;++i)
ans[i]=(q[i].lx==q[i].rx&&q[i].ly==q[i].ry)?0:0x3f3f3f3f;
solve(1,n,1,m,1,totq);
for(regi i=1;i<=totq;++i)
printf("%d\n",ans[i]);
return 0;
}