[ZJOI 2016]旅行者 Solution

题意:给一个矩阵,QQ个询问,每次询问两点最短路。
考虑最暴力的做法,每次直接暴力跑最短路,然后计算答案。这样的不足之处显然在于最短路上面,那么如何优化这个过程?
考虑计算所有的答案。
也就是分治,每次选择一条边,将它切开,考虑它对哪些询问有影响。

  • 如果一个询问的两个点必须经过这条线,那么最优答案肯定在这条线上,对这条线的每个点做一遍最短路。然后去更新这些询问的答案。
  • 如果一个点的两端点不需要经过这条线,最优答案也是有可能在这条线上的,还是对它们更新答案,然后继续分治下去。

考虑到时间问题,每次应该切开最长边,这样可以做最少次数的最短路。
code:code:

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章