【BZOJ】4456: [Zjoi2016]旅行者-最短路&二分

传送门:bzoj4456


题解

感觉自己对二分和分块还是不太熟悉啊。
此题可以离线,然后二分区域(长的边一分为二),每次让划分的两个区域边界上的每个点跑一遍当前整个区域的最短路,来更新还在当前区域内的询问的距离最小值。显然可以发现,对于起点终点都在同一边的询问,我们在接下来的二分当中还有可能更新到答案,其他的就不再下传。
此题貌似卡常,要用dijkstra+堆优化…
复杂度分析看这里(好复杂的样子,总之O(能过))


代码

#include
#include
#include
#include
#define rc(x,y) ((x-1)*m+y)
#define X(x) ((x-1)/m+1)
#define Y(x) ((x-1)%m+1)
using namespace std;
typedef long long ll;
const int N=1e5+50,M=5e6+10;
const int inf=2e9;int dis[N];
int n,m,a[N],cnt,Q[N],id[N],sum;
int head[N],to[M],nxt[M],w[M],tp[N],tot;

struct qr{
   int st,ed,ans;
   qr(){ans=inf;}
}q[N];
inline int rd()
{
    char ch=getchar();int x=0,f=1;
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
    return x*f;
}

inline void lk(int u,int v,int c)
{
   to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=c;
}

inline bool in(int x,int xa,int ya,int xb,int yb)
{return (X(x)>=xa && X(x)<=xb && Y(x)>=ya && Y(x)<=yb);}

inline void gd(int x)
{
    int y=x<<1,z=Q[x],t=dis[Q[x]];
    if(yy|1]]y]]) y|=1;
    while(y<=cnt && dis[Q[y]]x]=Q[y];id[Q[x]]=x;
        x=y;y=x<<1;
        if(yy|1]]y]]) y|=1;
    }
    Q[x]=z;id[z]=x;
}

inline void gu(int x)
{
    int z=Q[x],t=dis[Q[x]];
    while(x>1&& dis[Q[x>>1]]>t){Q[x]=Q[x>>1];id[Q[x]]=x;x>>=1;}
    Q[x]=z;id[z]=x;
}

inline void dij(int now,int xa,int ya,int xb,int yb)
{
    int i,j,x;cnt=1;Q[1]=now;id[now]=1;dis[now]=0;
    for(i=xa;i<=xb;++i)
     for(j=ya;j<=yb;++j) if(rc(i,j)!=now){
        x=rc(i,j);dis[x]=inf;Q[++cnt]=x;id[x]=cnt;
      }
    while(cnt){
        x=Q[1];Q[1]=Q[cnt--];id[Q[1]]=1;gd(1);
        for(i=head[x];i;i=nxt[i]){
            j=to[i];
            if(in(j,xa,ya,xb,yb) && dis[j]>dis[x]+w[i]){
                dis[j]=dis[x]+w[i];gu(id[j]);
            }   
        }
    }
}

inline void div(int xa,int ya,int xb,int yb,int L,int R)
{
    int mid,i,j,k,l,r;
    if(L>R) return;
    if(yb-ya>xb-xa){
        mid=(ya+yb)>>1;
        for(i=xa;i<=xb;++i) {
          dij(rc(i,mid),xa,ya,xb,yb);   
          for(j=L;j<=R;++j){
            k=a[j];
            q[k].ans=min(q[k].ans,dis[q[k].st]+dis[q[k].ed]);
          }
        }
        l=L-1;r=R+1;
        for(i=L;i<=R;++i){
            k=a[i];
            if(Y(q[k].st)q[k].ed)else if(Y(q[k].st)>mid && Y(q[k].ed)>mid) tp[--r]=k;
        }
        for(i=L;i<=l;++i) a[i]=tp[i];for(i=r;i<=R;++i) a[i]=tp[i];
        div(xa,ya,xb,mid-1,L,l);div(xa,mid+1,xb,yb,r,R);
    }else{
        mid=(xa+xb)>>1;
        for(i=ya;i<=yb;++i){
            dij(rc(mid,i),xa,ya,xb,yb);
            for(j=L;j<=R;++j){
                k=a[j];
                q[k].ans=min(q[k].ans,dis[q[k].st]+dis[q[k].ed]);
            }
        }
        l=L-1;r=R+1;
        for(i=L;i<=R;++i){
            k=a[i];
            if(X(q[k].st)q[k].ed)else if(X(q[k].st)>mid && X(q[k].ed)>mid) tp[--r]=k;
        }
        for(i=L;i<=l;++i) a[i]=tp[i];for(i=r;i<=R;++i) a[i]=tp[i];
        div(xa,ya,mid-1,yb,L,l);div(mid+1,ya,xb,yb,r,R);
    }
}

int main(){
   int i,j,zz,ix,iy;
   n=rd();m=rd();
   for(i=1;i<=n;++i)
    for(j=1;j<m;++j)
    {zz=rd();lk(rc(i,j),rc(i,j+1),zz);lk(rc(i,j+1),rc(i,j),zz);}
   for(i=1;ifor(j=1;j<=m;++j)
    {zz=rd();lk(rc(i,j),rc(i+1,j),zz);lk(rc(i+1,j),rc(i,j),zz);}
   sum=rd();
   for(i=1;i<=sum;++i){a[i]=i;ix=rd();iy=rd();q[i].st=rc(ix,iy);ix=rd();iy=rd();q[i].ed=rc(ix,iy);}
   div(1,1,n,m,1,sum);
   for(i=1;i<=sum;++i) printf("%d\n",q[i].ans);
}

你可能感兴趣的:(妙,最短路,二分)