[Zjoi2016]旅行者(分治+最短路)
4456:[Zjoi2016]旅行者
時間限制: 2000 ms 記憶體限制: 524288 KB
題目描述
小Y來到了一個新的城市旅行。她發現了這個城市的佈局是網格狀的,也就是有 條從東到西的道路和 條從南到北的道路,這些道路兩兩相交形成
輸入
第一行包含 2 個正整數
,
,表示城市的大小。
接下來n行,每行包含
個整數,第
行第
個正整數表示從一個路口到另一個路口的時間
。
接下來 行,每行包含 個整數,第 行第 個正整數表示從一個路口到另一個路口的時間 。
接下來一行,包含1個正整數 ,表示小Y的詢問個數。
接下來 行,每行包含4個正整數 ,表示兩個路口的位置。
輸出
輸出共 行,每行包含一個整數表示從一個路口到另一個路口最少需要花的時間。
輸入樣例
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[