4456:[Zjoi2016]旅行者
时间限制: 2000 ms 内存限制: 524288 KB
题目描述
小Y来到了一个新的城市旅行。她发现了这个城市的布局是网格状的,也就是有 n n 条从东到西的道路和条从南到北的道路,这些道路两两相交形成 n n ×个路口 ( i i ,)( 1≤i≤n 1 ≤ i ≤ n , 1≤j≤m 1 ≤ j ≤ m )。她发现不同的道路路况不同,所以通过不同的路口需要不同的时间。通过调查发现,从路口( i i ,)到路口( i i ,)需要时间 r(i,j) r ( i , j ) ,从路口 (i,j) ( i , j ) 到路口 (i+1,j) ( i + 1 , j ) 需要时间 c(i,j) c ( i , j ) 。注意这里的道路是双向的。小Y有 q q 个询问,她想知道从路口到路口 (x2,y2) ( x 2 , y 2 ) 最少需要花多少时间。
输入
第一行包含 2 个正整数
n
n
,,表示城市的大小。
接下来n行,每行包含
m−1
m
−
1
个整数,第
i
i
行第个正整数表示从一个路口到另一个路口的时间
r(i,j)
r
(
i
,
j
)
。
接下来 n−1 n − 1 行,每行包含 m m 个整数,第行第 j j 个正整数表示从一个路口到另一个路口的时间。
接下来一行,包含1个正整数 q q ,表示小Y的询问个数。
接下来行,每行包含4个正整数 x1,y1,x2,y2 x 1 , y 1 , x 2 , y 2 ,表示两个路口的位置。
输出
输出共 q q 行,每行包含一个整数表示从一个路口到另一个路口最少需要花的时间。
输入样例
2 2
2
3
6 4
2
1 1 2 2
1 2 2 1
输出样例
6
7
<script type="math/tex" id="MathJax-Element-775"> </script>
解:
一道很有意思的题。
首先两两最短路的复杂度难以接受,图的大小是很大的。于是这张图的性质就格外重要了。
这是张什么图呢?它是一张平面图。然而并没有什么卵用。其实题目告诉我这是一张网格图,题解告诉我网格可以分治。
这是一个套路
似乎被套路进去了。
那就这样吧。
考虑模仿树上点分治,我们用KD-tree的方法分割。左右两边点的最短路一定经过分割线。同属一边的点可能经过分割线。所以继续分治。这样我们就得到了答案。
一次分治如何统计答案?
对于每条分界线,它上面的点都做一下单源最短路。对于所有询问我们算一个答案,那么经过分界线的最优解已经算出来了,下面就递归,以后求最短路只用求不经过分界线的答案,也就是说图的大小减半,并且询问再两边的点可以从询问中删除了,因为它们之间的最短路一定经过分界线。
code:
//再写spfa是狗
#include<iostream>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<queue>
using namespace std;
struct xunwen{
int x1,y1,x2,y2,ans,tim;
}q[200005];
struct node{
int id,dis;
bool operator < (const node &QAQ) const{
return dis>QAQ.dis;
}
}yyy;
int n,m,a1,dis[200005],putout[200005],cnt;
int h[200005],li[200005];
bool vis[200005];
priority_queue <node> di;
//queue <xunwen> p1,p2,p3;
inline char gc(){
//static char buf[8000000], *p1 = buf, *p2 = buf;
//return (p1 == p2) && (p2 = (p1 = buf) + fread(buf, 1, 8000000, stdin), p1 == p2) ? EOF : *p1++;
return getchar();
}
void readit(int & a){
a=0;static char s=gc();
while(!isdigit(s)) s=gc();
while(isdigit(s)) a=a*10+s-'0',s=gc();
}
void spfa(const int &u,const int &d,const int &l,const int &r,const int &s){
for(int i=u;i<=d;i++)
for(int j=l;j<=r;j++)
dis[(i-1)*m+j]=0x3f3f3f3f;
yyy.id=s,yyy.dis=0;dis[s]=0;
di.push(yyy);
int now;
while(!di.empty()){
int now=di.top().id;di.pop();vis[now]=1;
int x=now/m+1,y=now%m;
if(!y) y=m,--x;
if(x!=d){
int to=x*m+y;
if(dis[to]>dis[now]+li[to]){
dis[to]=dis[now]+li[to];
yyy.id=to,yyy.dis=dis[to],di.push(yyy);
}
}
if(y!=r){
int to=(x-1)*m+y+1;
if(dis[to]>dis[now]+h[to]){
dis[to]=dis[now]+h[to];
yyy.id=to,yyy.dis=dis[to],di.push(yyy);
}
}
if(y!=l){
int to=(x-1)*m+y-1;
if(dis[to]>dis[now]+h[now]){
dis[to]=dis[now]+h[now];
yyy.id=to,yyy.dis=dis[to],di.push(yyy);
}
}
if(x!=u){
int to=(x-2)*m+y;
if(dis[to]>dis[now]+li[now]){
dis[to]=dis[now]+li[now];
yyy.id=to,yyy.dis=dis[to],di.push(yyy);
}
}
while(!di.empty()&&vis[di.top().id]==1) di.pop();
}
for(int i=u;i<=d;i++)
for(int j=l;j<=r;j++)
vis[(i-1)*m+j]=0;
}
void clr(const int &u,const int &d,const int &l,const int &r){
for(int i=u;i<=d;i++)
for(int j=l;j<=r;j++)
dis[(i-1)*m+j]=0x3f3f3f3f;
}
void div(const int &u,const int &d,const int &l,const int &r,const int &ql,const int &qr){
//cout<<u<<" "<<d<<" "<<l<<" "<<r<<" "<<ql<<" "<<qr<<endl;
if(ql>qr) return;
if((d-u)*(r-l)<=36){
for(int i=ql;i<=qr;i++){
//clr(u,d,l,r);
spfa(u,d,l,r,(q[i].x1-1)*m+q[i].y1);
q[i].ans=min(q[i].ans,dis[(q[i].x2-1)*m+q[i].y2]);
}
return;
}
if(u==d&&l==r){
for(int i=ql;i<=qr;i++) q[i].ans=0;
return;
}
if(d-u>=r-l){
int mid=(d+u)>>1;
//clr(u,d,l,r);
for(int i=l;i<=r;i++){
//for(int j=u;j<=d;j++)
//for(int k=l;k<=r;k++)
//dis[(j-1)*m+k]+=h[(mid-1)*m+i];
spfa(u,d,l,r,(mid-1)*m+i);
for(int j=ql;j<=qr;j++)
q[j].ans=min(q[j].ans,dis[(q[j].x1-1)*m+q[j].y1]+dis[(q[j].x2-1)*m+q[j].y2]);
}
int t1=ql-1,t2=qr+1;
for(int i=ql;i<=qr;i++){
if(q[i].x1<=mid&&q[i].x2<=mid){
if(t1!=i-1) swap(q[i],q[t1+1]);
t1++;
}
}
for(int i=qr;i>=ql;i--){
if(q[i].x1>mid&&q[i].x2>mid){
if(t2!=i+1) swap(q[i],q[t2-1]);
t2--;
}
}
div(u,mid,l,r,ql,t1),div(mid+1,d,l,r,t2,qr);
}
else{
int mid=(r+l)>>1;
//clr(u,d,l,r);
for(int i=u;i<=d;++i){
//for(int j=u;j<=d;++j)
//for(int k=l;k<=r;++k)
//dis[(j-1)*m+k]+=li[(i-1)*m+mid];
spfa(u,d,l,r,(i-1)*m+mid);
for(int j=ql;j<=qr;++j)
q[j].ans=min(q[j].ans,dis[(q[j].x1-1)*m+q[j].y1]+dis[(q[j].x2-1)*m+q[j].y2]);
}
int t1=ql-1,t2=qr+1;
for(int i=ql;i<=qr;i++){
if(q[i].y1<=mid&&q[i].y2<=mid){
if(t1!=i-1) swap(q[i],q[t1+1]);
t1++;
}
}
for(int i=qr;i>=ql;i--){
if(q[i].y1>mid&&q[i].y2>mid){
if(t2!=i+1) swap(q[i],q[t2-1]);
t2--;
}
}
div(u,d,l,mid,ql,t1),div(u,d,mid+1,r,t2,qr);
}
}
void write(int x) {
if(x / 10) write(x / 10);
putchar((x % 10) + '0');
}
int main()
{
readit(n), readit(m);
for(int i=1;i<=n;++i)
for(int j=1;j<m;++j)
readit(h[m*(i-1)+j+1]);
for(int i=1;i<n;++i)
for(int j=1;j<=m;++j)
readit(li[m*i+j]);
readit(a1);
for(int i=1;i<=a1;++i)
readit(q[i].x1),readit(q[i].y1),readit(q[i].x2),readit(q[i].y2),q[i].tim=i,q[i].ans=0x7f7f7f7f;
int t=a1;
for(int i=a1;i>=1;i--)
if(q[i].x1==q[i].x2&&q[i].y1==q[i].y2){
if(t!=i) swap(q[i],q[t]);
q[t].ans=0,t--;
}
div(1,n,1,m,1,t);
for(int i=1;i<=a1;++i)
putout[q[i].tim]=q[i].ans;
for(int i=1;i<=a1;++i)
write(putout[i]),putchar('\n');
}