1. 程式人生 > >[Zjoi2016]旅行者(分治+最短路)

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

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[