题解:P3350 [ZJOI2016] 旅行者

显然对每个点跑一次最短路不现实,我们考虑缩小每个点最短路的规模,而我们又是在一个矩形上,因而考虑分治。

由于题目中的限制是 \(n\times m\le 2\times 10^4\),所以 \(n\)\(m\) 中的较小值 \(\le\sqrt{nm}<150\),我们考虑对矩形的长边进行分治。

对于当前分治的矩形,对其包含的每个询问考虑与分界线(即 \(mid\) 这一列)的关系。

  • 如果询问的点对在分界线两侧,那么其最短路一定跨过分界线,枚举分界线上每个点,然后用该点到两个询问点的最短路更新即可,不必继续分治。
  • 否则该询问的两个点在分界线一侧,其答案可能经过分界线,那么和上面一样做即可;也可能不经过分界线,那么继续分治即可。

那么我们也是对每个点跑了最短路,但是每次跑的最短路只需考虑当前分治矩形内的点即可,而分治只有 \(O(\log \max \{n,m\})\) 层,其总复杂度为 \(O(nm\min \{n,m\}\log (nm)\log \max \{n,m\})\),但似乎比这个少一只 \(\log\)(?

Code:

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#define nd(x,y) (rev?((x)-1)*im+(y):((y)-1)*in+(x))
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,l,r) for(int i=(l);i>=(r);i--)
using namespace std;
const int maxs=2e4+5,maxn=155,maxq=1e5+5,inf=0x3f3f3f3f;
int dis[maxn][maxs];
int in,im,iq;
bool vis[maxs],rev;
typedef pair<int,int> pr;
vector<pr> G[maxs];
int an[maxq][2];
int ans[maxq];
struct node{
	int id,d;
	inline friend bool operator<(node a,node b){
		return a.d>b.d;
	}
};
priority_queue<node> pq;
inline void dijkstra(int sx,int sy,int l,int r){
	int lp=nd(1,l),rp=nd(in,r);
	rep(v1,lp,rp){
		dis[sx][v1]=inf;
		vis[v1]=false; 
	}
	dis[sx][nd(sx,sy)]=0;
	pq.push({nd(sx,sy),0});
	while(!pq.empty()){
		int cur=pq.top().id;
		pq.pop();
		if(vis[cur])continue;
		vis[cur]=true;
		for(pr p:G[cur]){
			int v=p.first,nw=dis[sx][cur]+p.second;
			if(v>=lp&&v<=rp&&nw<dis[sx][v]){
				dis[sx][v]=nw;
				pq.push({v,nw});
			}
		}
	}
}
inline void solve(int l,int r,vector<int> &v){
	if(v.empty())return;
	int mid=(l+r)>>1,lim=nd(in,mid);
	rep(v1,1,in)dijkstra(v1,mid,l,r);
	vector<int> vl,vr;
	for(int v1:v){
		int na=an[v1][0],nb=an[v1][1];
		rep(v2,1,in){
			ans[v1]=min(ans[v1],dis[v2][na]+dis[v2][nb]);
		}
		if(nb<=lim)vl.push_back(v1);
		if(na>lim)vr.push_back(v1);
	}
	
	if(l<r){
		solve(l,mid,vl);
		solve(mid+1,r,vr);
	}
}
int main()
{
//	freopen("P3350.in","r",stdin);
//	freopen("P3350.out","w",stdout);
	memset(ans,0x3f,sizeof(ans));
	cin>>in>>im;
	rev=(in>im);
	rep(v1,1,in)rep(v2,1,im-1){
		int iw;
		scanf("%d",&iw);
		G[nd(v1,v2)].emplace_back(nd(v1,v2+1),iw);
		G[nd(v1,v2+1)].emplace_back(nd(v1,v2),iw);
	}
	rep(v1,1,in-1)rep(v2,1,im){
		int iw;
		scanf("%d",&iw);
		G[nd(v1,v2)].emplace_back(nd(v1+1,v2),iw);
		G[nd(v1+1,v2)].emplace_back(nd(v1,v2),iw);
	}
	scanf("%d",&iq);
	rep(v1,1,iq){
		int x1,y1,x2,y2;
		scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
		an[v1][0]=nd(x1,y1);
		an[v1][1]=nd(x2,y2);
		if(an[v1][0]>an[v1][1])swap(an[v1][0],an[v1][1]);
	}
	if(in>im)swap(in,im);
	rev=false;
	vector<int> v;
	rep(v1,1,iq)v.push_back(v1);
	solve(1,im,v);
	rep(v1,1,iq)printf("%d\n",ans[v1]);
	return 0;
}
posted @ 2025-07-17 16:44  FugiPig  阅读(15)  评论(0)    收藏  举报