P3350 [ZJOI2016] 旅行者

liuboom發表於2024-12-06

P3350 [ZJOI2016] 旅行者

[ZJOI2016] 旅行者

題目描述

小 Y 來到了一個新的城市旅行。她發現了這個城市的佈局是網格狀的,也就是有 \(n\) 條從東到西的道路和 \(m\) 條從南到北的道路,這些道路兩兩相交形成 \(n\times m\) 個路口 \((i,j)\)\((1\leq i\leq n,1\leq j\leq m)\)

她發現不同的道路路況不同,所以透過不同的路口需要不同的時間。透過調查發現,從路口 \((i,j)\) 到路口 \((i,j+1)\) 需要時間 \(r(i,j)\) ,從路口 \((i,j)\) 到路口 \((i+1,j)\) 需要時間 \(c(i,j)\) 。注意這裡的道路是雙向的。小 Y 有 \(q\) 個詢問,她想知道從路口 \((x1,y1)\) 到路口 \((x2,y2)\) 最少需要花多少時間。

輸入格式

第一行包含 2 個正整數 \(n,m\) 表示城市的大小。

接下來 \(n\) 行,每行包含 \(m-1\) 個整數,第 \(i\) 行第 \(j\) 個正整數表示從一個路口到另一個路口的時間 \(r(i,j)\)

接下來 \(n-1\) 行,每行包含 \(m\) 個整數,第 \(i\) 行第 \(j\) 個正整數表示從一個路口到另一個路口的時間 \(c(i,j)\)

接下來一行,包含一個正整數 \(q\),表示小 Y 的詢問個數。

接下來 \(q\) 行,每行包含 \(4\) 個正整數 \(x1,y1,x2,y2\),表示兩個路口的位置。

輸出格式

輸出共 \(q\) 行,每行包含一個整數表示從一個路口到另一個路口最少需要花的時間。

資料規模與約定

  • \(n\times m \le 2\times 10^4\)
  • \(q \le 10^5\)
  • \(1 \le r(i,j),c(i,j) \le 10^4\)

首先我們能想到將操作離線
然後我就只會dijkstra了

首先我們注意到這張圖的所有邊要麼是縱向邊,要麼是橫向邊這不廢話嗎
在大佬的幫助下我們有些困難但還是成功的想到了分治:

假設我們現在對一個矩形內的所有答案進行統計:

mid
s (x,y) t

我們以(x,y)作為中轉,每一次取矩形較長的那一條邊,將其一分為二,然後遍歷中線上的每一個點,求出圖上儘可能多的點到這個點的距離然後統計答案

為什麼是儘可能多呢?
因為只有當前這個點在之前求最短路更新過,那麼之前的貢獻才會繼承到當前節點,顯然,每一次對一個節點(x,y):

它能直接更新距離的節點只有和它同行或者同列的節點(不然時間複雜度就爆炸了)剩下的節點只能來源於之前節點的繼承

我們再用一張圖來理解一下:

mid1
inf(t)
inf
mid2 - 5 4 3(s) 2 1 0(x,y) 1 2 3 4(x2,y2) - -
inf
inf
mid1
2(t)
1
mid2 - 9 8 7(s) 6 5 4(x,y) 3 2 1 0(x2,y2) 1 - -
1
2

顯然,經過兩次分治,我們得到了s->t的最短路
第一張圖中的點權是第一次分治時以(x,y)起點的點權
第二張圖中的點權是第二次分治時以(x2,y2)為起點的點權

然後我們只需要對於每次分治,先跑一次最短路,然後統計答案就好了

跑最短路時,如果當前起點的\(dis\not=inf\)則將當前矩形內之前跑過的最短路繼承下來

然後這題就做完了

Code:

#include<bits/stdc++.h>
#define id(x,y) ((x-1)*m+y)
const int N=2e4+5;
const int M=1e5+5;
const int inf=1e9;
using namespace std;
int n,m,q,e_cnt;
struct Edge{
	int to,nxt,w;
}e[N<<2];
struct Node{
	int x,y;
	bool operator<(const Node &x1)const{
		return x1.y<y;
	}
}a[N];
struct Que{
	int x1,y1,x2,y2;
}que[M];
int ans[M],dis[N],head[N];
void add(int x,int y,int w)
{
	e[++e_cnt]={y,head[x],w};
	head[x]=e_cnt;
}
priority_queue<Node> Q;
void dijkstra(int lx,int rx,int ly,int ry,int x,int y)
{
	if(dis[id(x,y)]!=inf)
	{
		for(int xx=lx;xx<=rx;xx++)for(int yy=ly;yy<=ry;yy++)
		{
			if(xx==x&&yy==y)continue;
			dis[id(xx,yy)]+=dis[id(x,y)];
		}
	}
	dis[id(x,y)]=0;
	Q.push({id(x,y),0});
	while(!Q.empty())
	{
		int u=Q.top().x;Q.pop();
		for(int i=head[u],v,w;i;i=e[i].nxt)
		{
			v=e[i].to,w=e[i].w;
			if((lx<=a[v].x&&a[v].x<=rx)&&(ly<=a[v].y&&a[v].y<=ry))
			{
				if(dis[v]>dis[u]+w)
				{
					dis[v]=dis[u]+w;
					Q.push({v,dis[v]});
				}	
			}
		}
	}
}
void solve(int lx,int rx,int ly,int ry,vector<int> ask)
{
	if(lx==rx&&ly==ry)
	{
		for(int id:ask)ans[id]=0;
		return ;
	}
	if(rx-lx>ry-ly)
	{
		int mid=lx+rx>>1;
		for(int xx=lx;xx<=rx;xx++)for(int yy=ly;yy<=ry;yy++){dis[id(xx,yy)]=inf;}
		for(int yy=ly;yy<=ry;yy++)
		{
			dijkstra(lx,rx,ly,ry,mid,yy);
			for(int i:ask){ans[i]=min(ans[i],dis[id(que[i].x1,que[i].y1)]+dis[id(que[i].x2,que[i].y2)]);}
		}
		vector<int> askl,askr;
		for(int i:ask)
		{
			if(max(que[i].x1,que[i].x2)<=mid)askl.push_back(i);
			if(mid<min(que[i].x1,que[i].x2))askr.push_back(i);
		}
		solve(lx,mid,ly,ry,askl);solve(mid+1,rx,ly,ry,askr);
	}
	else
	{
		int mid=ly+ry>>1;
		for(int xx=lx;xx<=rx;xx++)for(int yy=ly;yy<=ry;yy++){dis[id(xx,yy)]=inf;}
		for(int xx=lx;xx<=rx;xx++)
		{
			dijkstra(lx,rx,ly,ry,xx,mid);
			for(int i:ask){ans[i]=min(ans[i],dis[id(que[i].x1,que[i].y1)]+dis[id(que[i].x2,que[i].y2)]);}
		}
		vector<int> askl,askr;
		for(int i:ask)
		{
			if(max(que[i].y1,que[i].y2)<=mid)askl.push_back(i);
			if(mid<min(que[i].y1,que[i].y2))askr.push_back(i);
		}
		solve(lx,rx,ly,mid,askl);solve(lx,rx,mid+1,ry,askr);
	}
}
void init()
{
	for(int i=0;i<N;i++)dis[i]=inf;
	for(int i=0;i<M;i++)ans[i]=inf;
}
void work()
{
	init();
	cin>>n>>m;
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)a[id(i,j)]={i,j};
	for(int i=1;i<=n;i++)for(int j=1,w;j<m;j++){scanf("%d",&w);add(id(i,j),id(i,j+1),w);add(id(i,j+1),id(i,j),w);}
	for(int i=1;i<n;i++)for(int j=1,w;j<=m;j++){scanf("%d",&w);add(id(i,j),id(i+1,j),w);add(id(i+1,j),id(i,j),w);}
	cin>>q;
	vector<int> task;
	for(int i=1;i<=q;i++){scanf("%d%d%d%d",&que[i].x1,&que[i].y1,&que[i].x2,&que[i].y2);task.push_back(i);}
	//cout<<"build";
	solve(1,n,1,m,task);
	for(int i=1;i<=q;i++)printf("%d\n",ans[i]);
}
int main()
{
	//freopen("P3350.in","r",stdin);//freopen("P3350.out","w",stdout);
	work();
}