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();
}