BZOJ4456/UOJ#184[Zjoi2016]旅行者 分治 最短路
原文链接http://www.cnblogs.com/zhouzhendong/p/8682133.html
题目传送门 - BZOJ4456
题目传送门 - UOJ#184
题意
$n\times m$的网格图$q$次询问两个格子之间的最短路。
$n\times m\leq 2\times 10^4,q\leq 10^5$且任何两个相邻格子之间的路径长度$\leq 10^4$。
题解
考虑分治。
对于当前网格图以及起点和终点都在当前网格图内的询问进行处理。
考虑把当前网格图的长边作为分治对象。
我们来分割长边。对于分割线上的一条格子(我们称为中线),我们求得其他格子到他的最短路。然后用来更新答案。
把询问分成两种:
1. 起点和终点分别处于中线两侧的,必然经过中线。
2. 起点和终点在中线同一侧的,有可能经过中线,有可能不经过。
然后更新完之后就分治被中线分开的两块网格,继续更新第2种询问。
这题卡SPFA,最短路要写堆优化的Dijkstra。
%%%jiry_2!吉老师SPFA在UOJ上面80分!震惊。(蒟蒻自带大常数QAQ只有40)
代码
#include <cstring> #include <cstdio> #include <algorithm> using namespace std; const int N=20005,maxQ=100005,INF=1e9; struct Gragh{ static const int M=N*4; int cnt,y[M],z[M],nxt[M],fst[N]; void clear(){ cnt=0; memset(fst,0,sizeof fst); } void add(int a,int b,int c){ y[++cnt]=b,z[cnt]=c,nxt[cnt]=fst[a],fst[a]=cnt; } }g; int n,m,Q,id[maxQ],tmp[maxQ]; int HA(int a,int b){return (a-1)*m+b-1;} void HB(int v,int &a,int &b){a=v/m+1,b=v%m+1;} struct Query{ int s,t,ans; void get(){ int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); s=HA(x1,y1),t=HA(x2,y2); ans=s==t?0:INF; } }q[maxQ]; void buildg(){ g.clear(); int x; for (int i=1;i<=n;i++) for (int j=1;j<m;j++){ scanf("%d",&x); g.add(HA(i,j),HA(i,j+1),x); g.add(HA(i,j+1),HA(i,j),x); } for (int i=1;i<n;i++) for (int j=1;j<=m;j++){ scanf("%d",&x); g.add(HA(i,j),HA(i+1,j),x); g.add(HA(i+1,j),HA(i,j),x); } } int heap[N],pos[N],size,d[N]; void heap_up(int x){ for (int y=x>>1;y>0&&d[heap[x]]<d[heap[y]];x=y,y=x>>1) swap(pos[heap[x]],pos[heap[y]]),swap(heap[x],heap[y]); } void heap_down(int x){ for (int y=x<<1;y<=size;x=y,y=x<<1){ y|=y<size&&d[heap[y|1]]<d[heap[y]]; if (d[heap[y]]>d[heap[x]]) break; swap(pos[heap[x]],pos[heap[y]]),swap(heap[x],heap[y]); } } void heap_pop(){ pos[heap[1]]=0,heap[1]=heap[size--],pos[heap[1]]=1; heap_down(1); } void heap_push(int x){ if (!pos[x]) heap[pos[x]=++size]=x; heap_up(pos[x]); } void Dijkstra(int s,int x1,int y1,int x2,int y2){ int a,b,x,y; memset(pos,0,sizeof pos); memset(d,63,sizeof d); size=0; d[s]=0,heap_push(s); while (size){ x=heap[1]; heap_pop(); for (int i=g.fst[x];i;i=g.nxt[i]){ HB(y=g.y[i],a,b); if (x1<=a&&a<=x2&&y1<=b&&b<=y2&&d[y]>d[x]+g.z[i]){ d[y]=d[x]+g.z[i]; heap_push(y); } } } } void solve(int x1,int y1,int x2,int y2,int L,int R){ if (L>R) return; if (x2-x1>y2-y1){ int mid=(x1+x2)/2; for (int i=y1;i<=y2;i++){ Dijkstra(HA(mid,i),x1,y1,x2,y2); for (int j=L;j<=R;j++) q[id[j]].ans=min(q[id[j]].ans,d[q[id[j]].s]+d[q[id[j]].t]); } int Lr=L-1,Rl=R+1; for (int j=L;j<=R;j++){ int x3=q[id[j]].s/m+1,x4=q[id[j]].t/m+1; if (x3<mid&&x4<mid) tmp[++Lr]=id[j]; if (x3>mid&&x4>mid) tmp[--Rl]=id[j]; } for (int j=L;j<=Lr;j++) id[j]=tmp[j]; for (int j=Rl;j<=R;j++) id[j]=tmp[j]; solve(x1,y1,mid-1,y2,L,Lr); solve(mid+1,y1,x2,y2,Rl,R); } else { int mid=(y1+y2)/2; for (int i=x1;i<=x2;i++){ Dijkstra(HA(i,mid),x1,y1,x2,y2); for (int j=L;j<=R;j++) q[id[j]].ans=min(q[id[j]].ans,d[q[id[j]].s]+d[q[id[j]].t]); } int Lr=L-1,Rl=R+1; for (int j=L;j<=R;j++){ int y3=q[id[j]].s%m+1,y4=q[id[j]].t%m+1; if (y3<mid&&y4<mid) tmp[++Lr]=id[j]; if (y3>mid&&y4>mid) tmp[--Rl]=id[j]; } for (int j=L;j<=Lr;j++) id[j]=tmp[j]; for (int j=Rl;j<=R;j++) id[j]=tmp[j]; solve(x1,y1,x2,mid-1,L,Lr); solve(x1,mid+1,x2,y2,Rl,R); } } int main(){ scanf("%d%d",&n,&m); buildg(); scanf("%d",&Q); for (int i=1;i<=Q;i++) q[i].get(),id[i]=i; solve(1,1,n,m,1,Q); for (int i=1;i<=Q;i++) printf("%d\n",q[i].ans); return 0; }