[Zjoi2016]旅行者(分治+最短路)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvmaooi/article/details/82429159

4456:[Zjoi2016]旅行者

时间限制: 2000 ms 内存限制: 524288 KB

题目描述

小Y来到了一个新的城市旅行。她发现了这个城市的布局是网格状的,也就是有 n 条从东到西的道路和 m 条从南到北的道路,这些道路两两相交形成 n × m 个路口 ( i , j )( 1 i n , 1 j m )。她发现不同的道路路况不同,所以通过不同的路口需要不同的时间。通过调查发现,从路口( i , j )到路口( i , j + 1 )需要时间 r ( i , j ) ,从路口 ( i , j ) 到路口 ( i + 1 , j ) 需要时间 c ( i , j ) 。注意这里的道路是双向的。小Y有 q 个询问,她想知道从路口 ( x 1 , y 1 ) 到路口 ( x 2 , y 2 ) 最少需要花多少时间。

输入

第一行包含 2 个正整数 n , m ,表示城市的大小。
接下来n行,每行包含 m 1 个整数,第 i 行第 j 个正整数表示从一个路口到另一个路口的时间 r ( i , j )

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

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

接下来 q 行,每行包含4个正整数 x 1 , y 1 , x 2 , y 2 ,表示两个路口的位置。

输出

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

输入样例

2 2
2
3
6 4
2
1 1 2 2
1 2 2 1

输出样例

6
7












解:

一道很有意思的题。
首先两两最短路的复杂度难以接受,图的大小是很大的。于是这张图的性质就格外重要了。
这是张什么图呢?它是一张平面图。然而并没有什么卵用。其实题目告诉我这是一张网格图,题解告诉我网格可以分治。
这是一个套路
似乎被套路进去了。
那就这样吧。

考虑模仿树上点分治,我们用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');
}

猜你喜欢

转载自blog.csdn.net/lvmaooi/article/details/82429159