【BZOJ4456】[Zjoi2016]旅行者 分治+最短路

【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

Sample Output

6
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;
}
原文地址:https://www.cnblogs.com/CQzhangyu/p/7691157.html