【BZOJ4456】[Zjoi2016]旅行者
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
题解:能把各种各样奇怪的做法和最短路结合起来我也是服了~
思考怎么分治,如果矩形的x2-x1>y2-y1,那么我们就按x分治,因为此时一列的点数是小于sqrt(n)的,所以我们可以枚举分割线上的所有点,以这些点为源点都跑一次最短路,然后考虑每个询问:
如果询问的两个点在分治的不同侧,则最短路可能经过分割线上的每个点,用分割线上每个点到这两个点的距离和更新答案,然后这个询问我们就不用管了。
如果询问的两个点在分治的同侧,则最短路也可能经过分割线上的点,依旧要更新答案,然后将这个询问放到对应的分治结构去。
所以最终的复杂度是$O(n sqrt{n} log(n))$的。
#include <cstdio> #include <iostream> #include <algorithm> #include <queue> #include <cstring> #define P(A,B) ((A-1)*m+B) using namespace std; const int maxn=20010; int n,m,Q; int dis[maxn],v[maxn][4],vis[maxn],ans[100010]; int dx[]={1,0,-1,0},dy[]={0,1,0,-1}; struct query { int x1,y1,x2,y2,org; }q[100010],q1[100010],q2[100010]; struct node { int val,x,y; node() {} node(int a,int b,int c) {val=a,x=b,y=c;} bool operator < (const node &a) const { return val>a.val; } }; priority_queue<node> pq; void dijkstra(int sx,int sy,int lx,int rx,int ly,int ry) { int i,j,x,y,tx,ty; for(i=lx;i<=rx;i++) for(j=ly;j<=ry;j++) dis[P(i,j)]=1<<30,vis[P(i,j)]=0; dis[P(sx,sy)]=0,pq.push(node(0,sx,sy)); while(!pq.empty()) { x=pq.top().x,y=pq.top().y,pq.pop(); if(vis[P(x,y)]) continue; vis[P(x,y)]=1; for(i=0;i<4;i++) { tx=x+dx[i],ty=y+dy[i]; if(tx>=lx&&tx<=rx&&ty>=ly&&ty<=ry&&dis[P(tx,ty)]>dis[P(x,y)]+v[P(x,y)][i]) { dis[P(tx,ty)]=dis[P(x,y)]+v[P(x,y)][i]; pq.push(node(dis[P(tx,ty)],tx,ty)); } } } } void solve(int lx,int rx,int ly,int ry,int lq,int rq) { if(lq>rq) return ; if(lx==rx&&ly==ry) { for(int i=lq;i<=rq;i++) ans[q[i].org]=0; return ; } if(rx-lx>ry-ly) { int i,j,mid=(lx+rx)>>1,h1=0,h2=0; for(i=ly;i<=ry;i++) { dijkstra(mid,i,lx,rx,ly,ry); for(j=lq;j<=rq;j++) ans[q[j].org]=min(ans[q[j].org],dis[P(q[j].x1,q[j].y1)]+dis[P(q[j].x2,q[j].y2)]); } for(i=lq;i<=rq;i++) { if(q[i].x1<=mid&&q[i].x2<=mid) q1[++h1]=q[i]; if(q[i].x1>mid&&q[i].x2>mid) q2[++h2]=q[i]; } for(i=lq;i<=lq+h1-1;i++) q[i]=q1[i-lq+1]; for(i=lq+h1;i<=lq+h1+h2-1;i++) q[i]=q2[i-lq-h1+1]; solve(lx,mid,ly,ry,lq,lq+h1-1),solve(mid+1,rx,ly,ry,lq+h1,lq+h1+h2-1); } else { int i,j,mid=(ly+ry)>>1,h1=0,h2=0; for(i=lx;i<=rx;i++) { dijkstra(i,mid,lx,rx,ly,ry); for(j=lq;j<=rq;j++) ans[q[j].org]=min(ans[q[j].org],dis[P(q[j].x1,q[j].y1)]+dis[P(q[j].x2,q[j].y2)]); } for(i=lq;i<=rq;i++) { if(q[i].y1<=mid&&q[i].y2<=mid) q1[++h1]=q[i]; if(q[i].y1>mid&&q[i].y2>mid) q2[++h2]=q[i]; } for(i=lq;i<=lq+h1-1;i++) q[i]=q1[i-lq+1]; for(i=lq+h1;i<=lq+h1+h2-1;i++) q[i]=q2[i-lq-h1+1]; solve(lx,rx,ly,mid,lq,lq+h1-1),solve(lx,rx,mid+1,ry,lq+h1,lq+h1+h2-1); } } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } int main() { n=rd(),m=rd(); int i,j; for(i=1;i<=n;i++) for(j=1;j<m;j++) v[P(i,j)][1]=v[P(i,j+1)][3]=rd(); for(i=1;i<n;i++) for(j=1;j<=m;j++) v[P(i,j)][0]=v[P(i+1,j)][2]=rd(); Q=rd(); for(i=1;i<=Q;i++) q[i].x1=rd(),q[i].y1=rd(),q[i].x2=rd(),q[i].y2=rd(),q[i].org=i; memset(ans,0x3f,sizeof(ans)); solve(1,n,1,m,1,Q); for(i=1;i<=Q;i++) printf("%d ",ans[i]); return 0; }