P3350 [ZJOI2016] 旅行者
P3350 [ZJOI2016] 旅行者
[ZJOI2016] 旅行者
题目描述
小 Y 来到了一个新的城市旅行。她发现了这个城市的布局是网格状的,也就是有
她发现不同的道路路况不同,所以通过不同的路口需要不同的时间。通过调查发现,从路口
输入格式
第一行包含 2 个正整数
接下来
接下来
接下来一行,包含一个正整数
接下来
输出格式
输出共
数据规模与约定
。 。 。
首先我们能想到将操作离线
然后我就只会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)为起点的点权
然后我们只需要对于每次分治,先跑一次最短路,然后统计答案就好了
跑最短路时,如果当前起点的
然后这题就做完了
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(); }