P3350 [ZJOI2016] 旅行者

P3350 [ZJOI2016] 旅行者

[ZJOI2016] 旅行者

题目描述

小 Y 来到了一个新的城市旅行。她发现了这个城市的布局是网格状的,也就是有 n 条从东到西的道路和 m 条从南到北的道路,这些道路两两相交形成 n×m 个路口 (i,j)(1in,1jm)

她发现不同的道路路况不同,所以通过不同的路口需要不同的时间。通过调查发现,从路口 (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 行,每行包含 m1 个整数,第 i 行第 j 个正整数表示从一个路口到另一个路口的时间 r(i,j)

接下来 n1 行,每行包含 m 个整数,第 i 行第 j 个正整数表示从一个路口到另一个路口的时间 c(i,j)

接下来一行,包含一个正整数 q,表示小 Y 的询问个数。

接下来 q 行,每行包含 4 个正整数 x1,y1,x2,y2,表示两个路口的位置。

输出格式

输出共 q 行,每行包含一个整数表示从一个路口到另一个路口最少需要花的时间。

数据规模与约定

  • n×m2×104
  • q105
  • 1r(i,j),c(i,j)104

首先我们能想到将操作离线
然后我就只会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)为起点的点权

然后我们只需要对于每次分治,先跑一次最短路,然后统计答案就好了

跑最短路时,如果当前起点的disinf则将当前矩形内之前跑过的最短路继承下来

然后这题就做完了

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();
}
posted @   liuboom  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示