1. 程式人生 > 其它 >#分治,Dijkstra#洛谷 3350 [ZJOI2016]旅行者

#分治,Dijkstra#洛谷 3350 [ZJOI2016]旅行者

題目

給定一張\(n*m\)的網格圖,\(q\)次詢問兩點之間距離
\(n*m\leq 2*10^4,q\leq 10^5\)


分析

首先floyd會TLE,考慮兩點間距離可以由兩段拼湊起來,
那麼列舉中間點然後跑單源最短路,但是這樣與floyd時間複雜度無異,
一些中間點實際上完全不需要,考慮分治,每次選取中線上的點在子圖內跑單源最短路
據說時間複雜度是\(O(nm\sqrt{nm}\log{nm})\)


程式碼

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
const int N=20011,inf=0x3f3f3f3f; struct node{int y,w,next;}e[N<<2];
struct rec{int lx,ly,rx,ry,rk;}q[N*5],q1[N*5],q2[N*5];
struct Two{
	int d,x;
	inline bool operator <(const Two &t)const{
		return d<t.d;
	}
};
int as[N],Cnt,et=1,Q,dis[N],ans[N*5],n,m,Lx,Ly,Rx,Ry; Two heap[N];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
inline void add(int x,int y,int w){
	e[++et]=(node){y,w,as[x]},as[x]=et;
	e[++et]=(node){x,w,as[y]},as[y]=et; 
}
inline signed min(int a,int b){return a<b?a:b;}
inline void Push(Two w){
	heap[++Cnt]=w;
	rr int x=Cnt;
	while (x>1){
		if (heap[x]<heap[x>>1])
		    swap(heap[x],heap[x>>1]),x>>=1;
		else return; 
	}
}
inline void Pop(){
	heap[1]=heap[Cnt--];
	rr int x=1;
	while ((x<<1)<=Cnt){
		rr int y=x<<1;
		if (y<Cnt&&heap[y+1]<heap[y]) ++y;
		if (heap[y]<heap[x]) swap(heap[x],heap[y]),x=y;
		    else return;
	}
}
inline signed rk(int x,int y){return (x-1)*m+y;}
inline bool Into(int Rk){
	rr int x=(Rk-1)/m+1,y=Rk-(x-1)*m;
	return Lx<=x&&x<=Rx&&Ly<=y&&y<=Ry;
}
inline void Dijkstra(int S){
	if (dis[S]){
		for (rr int i=Lx;i<=Rx;++i)
	    for (rr int j=Ly;j<=Ry;++j)
		    if (rk(i,j)!=S) dis[rk(i,j)]+=dis[S];
	}else for (rr int i=Lx;i<=Rx;++i)
	    for (rr int j=Ly;j<=Ry;++j)
	        dis[rk(i,j)]=inf;
	heap[++Cnt]=(Two){0,S},dis[S]=0;
	while (Cnt){
		rr Two t=heap[1];
		Pop(); if (t.d!=dis[t.x]) continue;
		for (rr int i=as[t.x];i;i=e[i].next)
		if (Into(e[i].y)&&dis[e[i].y]>dis[t.x]+e[i].w){
			dis[e[i].y]=dis[t.x]+e[i].w;
			Push((Two){dis[e[i].y],e[i].y});
		}
	}
}
inline void dfs(int lx,int rx,int ly,int ry,int l,int r){
	if (l>r) return;
	if (lx==rx&&ly==ry){
		for (rr int i=l;i<=r;++i) ans[q[i].rk]=0;
		return;
	}
	Lx=lx,Rx=rx,Ly=ly,Ry=ry;
	if (rx-lx>ry-ly){
		rr int mid=(lx+rx)>>1,tot1=0,tot2=0;
		for (rr int i=ly;i<=ry;++i){
			Dijkstra(rk(mid,i));
			for (rr int j=l;j<=r;++j)
			    ans[q[j].rk]=min(ans[q[j].rk],dis[rk(q[j].lx,q[j].ly)]+dis[rk(q[j].rx,q[j].ry)]);
		}
		for (rr int i=l;i<=r;++i){
			if (lx<=q[i].lx&&q[i].lx<=mid&&lx<=q[i].rx&&q[i].rx<=mid) q1[++tot1]=q[i];
			if (mid+1<=q[i].lx&&q[i].lx<=rx&&mid+1<=q[i].rx&&q[i].rx<=rx) q2[++tot2]=q[i];
		}
		for (rr int i=1;i<=tot1;++i) q[i+l-1]=q1[i];
		for (rr int i=1;i<=tot2;++i) q[r-i+1]=q2[i];
		dfs(lx,mid,ly,ry,l,l+tot1-1);
		dfs(mid+1,rx,ly,ry,r-tot2+1,r);
    }else{
 		rr int mid=(ly+ry)>>1,tot1=0,tot2=0;
		for (rr int i=lx;i<=rx;++i){
			Dijkstra(rk(i,mid));
			for (rr int j=l;j<=r;++j)
			    ans[q[j].rk]=min(ans[q[j].rk],dis[rk(q[j].lx,q[j].ly)]+dis[rk(q[j].rx,q[j].ry)]);
		}
		for (rr int i=l;i<=r;++i){
			if (ly<=q[i].ly&&q[i].ly<=mid&&ly<=q[i].ry&&q[i].ry<=mid) q1[++tot1]=q[i];
			if (mid+1<=q[i].ly&&q[i].ly<=ry&&mid+1<=q[i].ry&&q[i].ry<=ry) q2[++tot2]=q[i];
		}
		for (rr int i=1;i<=tot1;++i) q[i+l-1]=q1[i];
		for (rr int i=1;i<=tot2;++i) q[r-i+1]=q2[i];
		dfs(lx,rx,ly,mid,l,l+tot1-1);
		dfs(lx,rx,mid+1,ry,r-tot2+1,r);   	
	}
}
signed main(){
	n=iut(),m=iut();
	for (rr int i=1;i<=n;++i)
	for (rr int j=1;j<m;++j)
		add(rk(i,j),rk(i,j+1),iut());
	for (rr int i=1;i<n;++i)
	for (rr int j=1;j<=m;++j)
	    add(rk(i,j),rk(i+1,j),iut());
	Q=iut();
	for (rr int i=1;i<=Q;++i)
	    ans[i]=inf,q[i]=(rec){iut(),iut(),iut(),iut(),i};
	dfs(1,n,1,m,1,Q);
	for (rr int i=1;i<=Q;++i) print(ans[i]),putchar(10);
	return 0;
}