[ZJOI2016]旅行者 题解

整体二分+最短路

Statement

网格图,有边权,多次询问两点距离

\(n\times m\le 2\times 10^4,q\le 10^5,w\le 10^4\)

[ZJOI2016]旅行者 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

Solution

容易处理只有一个询问的情况。多个询问,网格图考虑分治

对于当前矩形,将长边切开,对于切开的这条线上的所有点跑单源最短路然后贡献答案

对于在同一边的询问,递归即可,不在同一边的则答案已知

容易发现这样的复杂度是 \(T(n)=2T(n/2)+\sqrt n\log n =O(n\sqrt n \log n)\) ,此处 \(n\) 指网格图大小

有点卡常,有一个优化是每次跑最短路的时候把初始距离设为 \(dis[x]+dis[st]\)\(x\) 为当前点,\(st\) 为当前起点

时间复杂度具体证明:复杂度

Code

#include<bits/stdc++.h>
#define id(x,y) ((x-1)*m+y)
// #define int register int
using namespace std;
const int N = 2e4+5;
const int M = 2e5+5;

char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read(){
    int s=0,w=1; char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
    return s*w;
}
void print(long long x) {
    if(x>9) print(x/10);
    *O++=x%10+'0';
}
bool cmin(int &a,int b){return a>b?a=b,1:0;}
bool cmax(int &a,int b){return a<b?a=b,1:0;}

struct Edge{int nex,to,dis;}edge[N<<3];
struct Item{int a,b,c,d,pos,ans;}qs[M],tl[M],tr[M],t[M];
int head[N],dis[N],q[(int)2e7+5];
bool vis[N],fg[N];
int n,m,Q,elen=1;

void addedge(int u,int v,int w){
    edge[++elen]=(Edge){head[u],v,w},head[u]=elen;
    edge[++elen]=(Edge){head[v],u,w},head[v]=elen;
}
void spfa(int l1,int r1,int l2,int r2,int st){
    for(int i=l1;i<=r1;++i)
        for(int j=l2;j<=r2;vis[id(i,j)]=0,++j)
            if(!dis[st])dis[id(i,j)]=0x3fffffff;
            else dis[id(i,j)]+=dis[st];
    int l=0,r=0; dis[st]=0,q[r++]=st;
    while(l<r){
        int u=q[l++]; vis[u]=0;
        for(int e=head[u],v;v=edge[e].to,e;e=edge[e].nex)
            if(fg[v]&&dis[v]>dis[u]+edge[e].dis){
                dis[v]=dis[u]+edge[e].dis;
                if(!vis[v])vis[v]=1,q[r++]=v;
            }
                
    }
}
void solve(int l1,int r1,int l2,int r2,int l,int r){
    // printf("%d %d %d %d %d %d\n",l1,r1,l2,r2,l,r);
    if(l>r||l1==r1&&l2==r2)return ;
    if(r1-l1>r2-l2){
        int mid=(l1+r1)/2;
        for(int i=l1;i<=r1;++i)
            for(int j=l2;j<=r2;++j)fg[id(i,j)]=1;
        for(int i=l2;i<=r2;++i){
            spfa(l1,r1,l2,r2,id(mid,i));
            for(int j=l;j<=r;++j)
                cmin(qs[j].ans,dis[id(qs[j].a,qs[j].b)]+dis[id(qs[j].c,qs[j].d)]);
        }
        for(int i=l1;i<=r1;++i)
            for(int j=l2;j<=r2;++j)fg[id(i,j)]=dis[id(i,j)]=0;
        int c1=0,c2=0,c3=0;
        for(int i=l;i<=r;++i)
            if(qs[i].a<=mid&&qs[i].c<=mid)tl[++c1]=qs[i];
            else if(qs[i].a>mid&&qs[i].c>mid)tr[++c2]=qs[i];
            else t[++c3]=qs[i];
        for(int i=l;i<=l+c1-1;++i)qs[i]=tl[i-l+1];
        for(int i=l+c1;i<=l+c1+c2-1;++i)qs[i]=tr[i-l-c1+1];
        for(int i=l+c1+c2;i<=r;++i)qs[i]=t[i-l-c1-c2+1];
        solve(l1,mid,l2,r2,l,l+c1-1);
        solve(mid+1,r1,l2,r2,l+c1,l+c1+c2-1);
    }else{
        int mid=(l2+r2)/2;
        for(int i=l1;i<=r1;++i)
            for(int j=l2;j<=r2;++j)fg[id(i,j)]=1;
        for(int i=l1;i<=r1;++i){
            spfa(l1,r1,l2,r2,id(i,mid));
            for(int j=l;j<=r;++j)
                cmin(qs[j].ans,dis[id(qs[j].a,qs[j].b)]+dis[id(qs[j].c,qs[j].d)]);
        }
        for(int i=l1;i<=r1;++i)
            for(int j=l2;j<=r2;++j)fg[id(i,j)]=dis[id(i,j)]=0;
        int c1=0,c2=0,c3=0;
        for(int i=l;i<=r;++i)
            if(qs[i].b<=mid&&qs[i].d<=mid)tl[++c1]=qs[i];
            else if(qs[i].b>mid&&qs[i].d>mid)tr[++c2]=qs[i];
            else t[++c3]=qs[i];
        for(int i=l;i<=l+c1-1;++i)qs[i]=tl[i-l+1];
        for(int i=l+c1;i<=l+c1+c2-1;++i)qs[i]=tr[i-l-c1+1];
        for(int i=l+c1+c2;i<=r;++i)qs[i]=t[i-l-c1-c2+1];
        solve(l1,r1,l2,mid,l,l+c1-1);
        solve(l1,r1,mid+1,r2,l+c1,l+c1+c2-1);
    }
}

signed main(){
    n=read(),m=read();
    for(int i=1;i<=n;++i)for(int j=1;j<m;++j)
        addedge(id(i,j),id(i,j+1),read());
    for(int i=1;i<n;++i)for(int j=1;j<=m;++j)
        addedge(id(i,j),id(i+1,j),read());
    Q=read();
    for(int i=1;i<=Q;++i)
        qs[i]=(Item){read(),read(),read(),read(),i,0x7fffffff};
    solve(1,n,1,m,1,Q);
    sort(qs+1,qs+1+Q,[](Item a,Item b)
        {return a.pos<b.pos;});
    for(int i=1;i<=Q;++i)
        print(qs[i].ans),*O++='\n';
    fwrite(obuf,O-obuf,1,stdout);
    return 0;
}
posted @ 2022-03-31 16:34  _Famiglistimo  阅读(62)  评论(0编辑  收藏  举报