題意:給一個矩陣,個詢問,每次詢問兩點最短路。
考慮最暴力的做法,每次直接暴力跑最短路,然後計算答案。這樣的不足之處顯然在於最短路上面,那麼如何優化這個過程?
考慮計算所有的答案。
也就是分治,每次選擇一條邊,將它切開,考慮它對哪些詢問有影響。
- 如果一個詢問的兩個點必須經過這條線,那麼最優答案肯定在這條線上,對這條線的每個點做一遍最短路。然後去更新這些詢問的答案。
- 如果一個點的兩端點不需要經過這條線,最優答案也是有可能在這條線上的,還是對它們更新答案,然後繼續分治下去。
考慮到時間問題,每次應該切開最長邊,這樣可以做最少次數的最短路。
#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;
}