ZJOI旅行者【题解】

前言

为什么分治?
首先是一个同学的ppt里有这个题,那里面有一句话——可以离线.这就说明八成是一个分治。
然后这种问题,如果询问少肯定还会考虑一下dp。然后一个显然的性质是每次都会经过他们两中间的那条轴线.一个基于此而又看似暴力的做法就诞生了

sol

一个比较暴力易想的办法是,如果对于一个点,知道它到所有的点的最短路,那么这个点如果是某两点的必经之路,那么显然他到这两点的最短路之和就是这两点的最短路。有发现这样无法保证一定经过这个点,但可以发现可以换成枚举一条线,如果两点在线的两边,那么必然经过这条线。这样就可以枚举线上的所有点。
有一个显然的问题是,无法确定必经哪一条线,如果对于整体只枚举一条线的话。所以对于整体显然要枚举多条线,粗略一看这个线数是边长的。线上的点也是边长的。这样不行。
但任何一个普通oier都知道,这玩意二分一下,n就成log。每次都选中间的线。还有一个问题就是选哪一条.
我们发现不管选哪一条,只要选中间的,都可以把点数均分成两份。但如果选短的那一条,线上枚举的点数就会更少.所以每次都选短的那一条.
写起来,码量稍大,而且要想清楚.
这道题复杂度既有根号还有log,对于常数也有一定要求.但只要不写丑,只要不追求上榜,什么优化都不要加,用一个O(2)就可以了。dijskra与spfa,都可以.

code

// luogu-judger-enable-o2
#include
using namespace std;
template 
inline void read(T&data){
    data=0;
    register char ch=0;
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch<='9'&&ch>='0'){
        data=(data<<3)+(data<<1)+(ch^48);
        ch=getchar();
    }
    return;
}
template 
inline void write(R data){
    if(data>9)write(data/10);
    putchar(data%10+'0');
}
const int INF = 2e9;
const int _ =20033;
const int __ =200033;
int n,m,rex[20033],rey[20033];
inline int id(register int x,register int y){return (x-1)*m+y;}
int cnt_edge,dist[_],to[__],nxt[__],fir[_],val[__],q_sum,ans[__];
struct zjy{
    int x1,y1,x2,y2,id;
}que[100001],que1[100001],que2[100001];
struct node{
    int dist,point;
    bool operator < (const node &a)const {
        return dist>a.dist;
    }
};
/*bool operator < (node ug, ode gu) {
    return ug.dist>gu.dist;
    } */
void dijkstra(register int now,register int x1,register int y1,register int x2,register int y2){
    //memset(dist,0x4f,sizeof(dist));
    for(register int i=x1;i<=x2;++i){
        for(register int j=y1;j<=y2;++j){
            dist[id(i,j)]=INF;
        }
    }
    dist[now]=0;
    priority_queue Q;
    while(!Q.empty())Q.pop();
    dist[now]=0;Q.push((node){0,now});
    while(!Q.empty()){
        register node ft=Q.top();Q.pop();
        for(register int i=fir[ft.point];i;i=nxt[i]){
            if(rex[to[i]]x2||rey[to[i]]y2)continue;
            if(dist[to[i]]>dist[ft.point]+val[i]){
                dist[to[i]]=dist[ft.point]+val[i];
                Q.push((node){dist[to[i]],to[i]});
            }
        }
    }
}
void solve(register int x1,register int y1,register int x2,register int y2,register int l,register int r){
    //cout<
    if(l>r)return;
    if(x2-x1>y2-y1){
        //cout<<"左边"<
        register int t1=0,t2=0,mid=(x1+x2)>>1;
        for(register int i=y1;i<=y2;++i){
            dijkstra(id(mid,i),x1,y1,x2,y2);
            for(register int j=l;j<=r;++j){
                ans[que[j].id]=min(ans[que[j].id],dist[id(que[j].x1,que[j].y1)]+dist[id(que[j].x2,que[j].y2)]);
                //if(que[j].id==3){cout<<"小胖最胖"<<' '<
            }
        }
        for(register int i=l;i<=r;++i){
            if(que[i].x1<=mid&&que[i].x2<=mid)
                que1[++t1]=que[i];          
            if(que[i].x1>mid&&que[i].x2>mid)
                que2[++t2]=que[i];          
        }
        for(register int i=1;i<=t1;++i)
            que[i+l-1]=que1[i];     
        for(register int i=1;i<=t2;++i)
            que[t1+l-1+i]=que2[i];
        solve(x1,y1,mid,y2,l,l+t1-1);
        solve(mid+1,y1,x2,y2,t1+l,t1+t2+l-1);
    }
    else {
        //cout<<"右边"<
        register int t1=0,t2=0,mid=(y1+y2)>>1;//cout<
        for(register int i=x1;i<=x2;++i){
            dijkstra(id(i,mid),x1,y1,x2,y2);
            for(register int j=l;j<=r;++j){
                ans[que[j].id]=min(ans[que[j].id],dist[id(que[j].x1,que[j].y1)]+dist[id(que[j].x2,que[j].y2)]);
                //if(que[j].id==3){cout<<"小胖最强"<<' '<
            }
        }
        for(register int i=l;i<=r;++i){
            if(que[i].y1<=mid&&que[i].y2<=mid)
                que1[++t1]=que[i];          
            if(que[i].y1>mid&&que[i].y2>mid)
                que2[++t2]=que[i];          
        }
        for(register int i=1;i<=t1;++i)
            que[i+l-1]=que1[i];     
        for(register int i=1;i<=t2;++i)
            que[t1+l-1+i]=que2[i];
        solve(x1,y1,x2,mid,l,l+t1-1);
        solve(x1,mid+1,x2,y2,t1+l,t1+t2+l-1);
    }
}
inline void add_edge(register int a,register int b,register int zh){to[++cnt_edge]=a,nxt[cnt_edge]=fir[b],val[cnt_edge]=zh,fir[b]=cnt_edge;}
int main(){
    //freopen("data.in","r",stdin);
    //freopen("1.out","w",stdout);
    read(n),read(m);
    register int a,b,c,d;
    for(register int i=1;i<=n;++i)
        for(register int j=1;j<=m;++j)
            rex[(i-1)*m+j]=i,rey[(i-1)*m+j]=j;
    for(register int i=1;i<=n;++i)
        for(register int j=1;jid(i,j),id(i,j+1),a),add_edge(id(i,j+1),id(i,j),a);
    for(register int i=1;ifor(register int j=1;j<=m;++j)
            read(a),add_edge(id(i,j),id(i+1,j),a),add_edge(id(i+1,j),id(i,j),a);
    read(q_sum);register int cc=0;
    memset(ans,0x4f,sizeof(ans));
    for(register int i=1;i<=q_sum;++i){
        read(a),read(b),read(c),read(d);
        if((a==c)&&(b==d))ans[i]=0;
        else que[++cc]=(zjy){a,b,c,d,i};
    }

    solve(1,1,n,m,1,cc);
    for(register int i=1;i<=q_sum;++i){
        //printf("%d")
        write(ans[i]);puts("");
    }
}

你可能感兴趣的:(题解,分治)