【分治+最短路】UOJ#184【ZJOI2016】旅行者

原题地址

【题目大意】
有n条横向道路,m条纵向道路,形成了一些交点。相邻两个交点之间道路长为wij,现在给出q个询问,每次询问两个交点之间的最短路径。
n×m2×104,q105 n × m ≤ 2 × 10 4 , q ≤ 10 5

【题目分析】
我已经嗅到了分治的气息。
当然也可能是分块。
(我更喜欢前者)

【解题思路】
看到这种多个询问,n又不大的东西,第一时间想到的就是分治,显然分治里面必然是套一个最短路。

考虑如何分治,首先还是把q标号以后离线操作,考虑对于一个矩形 x[lx,rx] x ∈ [ l x , r x ] y[ly,ry] y ∈ [ l y , r y ] ,然后我们就要处理出S和T都在这个矩形内部的最短路,而且路径不超出这个区间。

不妨设 rxlx>ryly r x − l x > r y − l y ,那么我们最优情况应当是对x进行分治,则将这个区间分成了两块,对于每个询问有两种情况:

1.起点和终点分别在两个块,显然最短路会经过mid。
2起点和终点在同一块,那么最短路可能经过mid,也可能只在子区块内,这样就可以递归到下一层。

那么对于每个块,我们就要求出到mid上所有点的最短路径,这个用dijkstra跑一下就行了。其他就交给分治处理吧。

时间复杂度 O(n1.5logn) O ( n 1.5 l o g n ) ,具体证明我不会,看这里吧

【代码】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;

const int INF=1e9+10;
const int MAXN=2e4+10;
const int MAXQ=1e5+10;

int n,m,qs,tot,cnt;
int head[MAXQ],ans[MAXQ],dis[MAXQ<<1];
int ids[MAXQ];
//int q[MAXQ];

struct Tway
{
    int pnt,nex,w;
};
Tway e[MAXQ<<2];

struct Tpoint
{
    int sx,sy,dx,dy,id;
};
Tpoint a[MAXQ],b[MAXQ];

struct QUQ
{
    int d,ids;

    QUQ(){}
    QUQ(int dd,int idss)
    {
        d=dd;ids=idss;
    }
};

bool operator < (const QUQ &A,const QUQ &B)
{
    return A.d>B.d;
}

inline int read()
{  
    int x=0; char ch=getchar();  
    while (ch<'0' || ch>'9') ch=getchar();  
    while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }  
    return x;  
}  

inline int getp(int x,int y)
{
    return (x-1)*m+y;
}

inline void add(int u,int v,int w)
{
    ++tot;
    e[tot].pnt=v;e[tot].w=w;
    e[tot].nex=head[u];head[u]=tot;
}

inline void init()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<m;++j)
        {
            int x,u,v;
            x=read();
            u=getp(i,j);v=getp(i,j+1);
            add(u,v,x);add(v,u,x);
//          printf("u:%d v:%d\n",u,v);  
        }
    }

    for(int i=1;i<n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            int x,u,v;
            x=read();
            u=getp(i,j);v=getp(i+1,j);
            add(u,v,x);add(v,u,x);
//          printf("u:%d v:%d\n",u,v);
        }
    }

    qs=read();
    for(int i=1;i<=qs;++i)
    {
        a[i].sx=read();a[i].sy=read();
        a[i].dx=read();a[i].dy=read();
//      scanf("%d%d%d%d",&a[i].sx,&a[i].sy,&a[i].dx,&a[i].dy);
        a[i].id=i;ans[i]=INF;
    }

/*  printf("ways:\n");
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
        {
            int t=getp(i,j);
            printf("%d:",t);
            for(int p=head[t];p;p=e[p].nex)
                printf("%d ",e[p].pnt);
            printf("\n");
        }
    printf("\n\n");*/
}


/*inline void pup(int x)
{
    int y=q[x],t=dis[y];
    for(;x>1 && t<dis[q[x>>1]];x>>=1)
    {
        q[x]=q[x>>1];
        ids[q[x]]=x;
    }
    q[x]=y;ids[y]=x;
}

inline void pud(int x)
{
    int y=x<<1,z=q[x],t=dis[z];  
    if (y<cnt && dis[q[y|1]]<dis[q[y]]) 
        y|=1;  
    while (y<=cnt && dis[q[y]]<t)
    {  
        q[x]=q[y]; ids[q[x]]=x;  
        x=y; y<<=1; 
        if (y<cnt && dis[q[y|1]]<dis[q[y]]) 
            y|=1;  
    }  
    q[x]=z;ids[z]=x;  
}

inline void dij(int sta,int x,int xx,int y,int yy)
{
    cnt=1;
    q[1]=sta; ids[sta]=1; dis[sta]=0;  
    for(int i=x;i<=xx;++i)  
        for(int j=y;j<=yy;++j)
        {  
            int t=getp(i,j);  
            if (t!=sta)
            { 
                dis[t]=INF; 
                q[++cnt]=t; 
                ids[t]=cnt;
            }  
        }  
    while (cnt)
    {  
        int u=q[1]; q[1]=q[cnt--]; ids[q[1]]=1; pud(1);  
//      printf("u:%d\n",u);

        for(int i=head[u];i;i=e[i].nex)
        {
            int v=e[i].pnt;
            int px=(v-1)/m+1,py=(v-1)%m+1;
//          printf("%d %d %d\n",px,py,i);
            if(px>=x && px<=xx && py>=y && py<=yy && dis[u]+e[i].w<dis[v])
            {
                dis[v]=dis[u]+e[i].w;
                pup(ids[v]);
            }
        }
    }  

    for(int i=x;i<=xx;++i)  
        for(int j=y;j<=yy;++j)
        {  
            int x=getp(i,j);  
            printf("%d ",dis[x]);
        }  
    printf("\n");
}*/

inline void dij(int sta,int x,int xx,int y,int yy)
{   
    for(int i=x;i<=xx;++i)
        for(int j=y;j<=yy;++j)
            dis[getp(i,j)]=INF;
    dis[sta]=0;
    priority_queue<QUQ>q;
    q.push(QUQ{0,sta});

    while(!q.empty())
    {
        int u=q.top().ids,dp=q.top().d;
        q.pop();

        for(int i=head[u];i;i=e[i].nex)
        {
            int pn=e[i].pnt;
            int px=(pn-1)/m+1,py=(pn-1)%m+1;
            if(px>=x && px<=xx && py>=y && py<=yy && dis[u]+e[i].w<dis[pn])
            {
                dis[pn]=dis[u]+e[i].w;
                q.push(QUQ{dis[pn],pn});
            }
        }
    }

/*  for(int i=x;i<=xx;++i)  
        for(int j=y;j<=yy;++j)
        {  
            int x=getp(i,j);  
            printf("%d ",dis[x]);
        }  
    printf("\n");*/
}

inline void solve(int x,int xx,int y,int yy,int l,int r)
{
//  printf("%d %d %d %d %d %d\n",x,xx,y,yy,l,r);
    if(l>r)
        return;
    if(xx-x>yy-y)
    {
        int mid=(x+xx)>>1;
        for(int i=y;i<=yy;++i)
        {
            dij(getp(mid,i),x,xx,y,yy);
            for(int j=l;j<=r;++j)
                ans[a[j].id]=min(ans[a[j].id] , dis[getp(a[j].sx,a[j].sy)] + dis[getp(a[j].dx,a[j].dy)]);
        }

        int j=l-1,k=r+1;
        for(int i=l;i<=r;++i)
            if(a[i].sx<mid && a[i].dx<mid)
                b[++j]=a[i];
            else
            if(a[i].sx>mid && a[i].dx>mid)
                b[--k]=a[i];

        for(int i=l;i<=j;++i)
            a[i]=b[i];
        solve(x,mid-1,y,yy,l,j);

        for(int i=k;i<=r;++i)
            a[i]=b[i];
        solve(mid+1,xx,y,yy,k,r);
    }
    else
    {
        int mid=(y+yy)>>1;
        for(int i=x;i<=xx;++i)
        {
            dij(getp(i,mid),x,xx,y,yy);
            for(int j=l;j<=r;++j)
                ans[a[j].id]=min(ans[a[j].id] , dis[getp(a[j].sx,a[j].sy)] + dis[getp(a[j].dx,a[j].dy)]);
        }

        int j=l-1,k=r+1;
        for(int i=l;i<=r;++i)
            if(a[i].sy<mid && a[i].dy<mid)
                b[++j]=a[i];
            else
            if(a[i].sy>mid && a[i].dy>mid)
                b[--k]=a[i];

        for(int i=l;i<=j;++i)
            a[i]=b[i];
        solve(x,xx,y,mid-1,l,j);

        for(int i=k;i<=r;++i)
            a[i]=b[i];
        solve(x,xx,mid+1,yy,k,r);
    }
}

inline void print_ans()
{
    for(int i=1;i<=qs;++i)
        printf("%d\n",ans[i]);
}

int main()
{
    freopen("UOJ184.in","r",stdin);
    freopen("UOJ184.out","w",stdout);

    init();
    solve(1,n,1,m,1,qs);
    print_ans();

    return 0;
}

【总结】
这题我写挂了好久,开始用的STL的堆,然后TLE8个点(只A了2个小数据),我想不应该啊,于是手写了,但还是T。

思考良久后发现读入挂了,读入的方式貌似有点问题(居然能过两个点??),改了还是T。

百思不得其解后,发现空间没开够。(UOJ居然是T??,不是RE吗??)改了以后手写和STL都过了。

后者10个点一共慢了1s左右。
但是我懒得手写啊

小错误太多了!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值