找回密码
 立即注册
首页 业界区 科技 [25BJWCB2A]最优化(网格图最短路)

[25BJWCB2A]最优化(网格图最短路)

轧岔 2025-6-9 19:46:27
题目大意

详细题目传送门
给一个 \(m\) 行 \(n\) 列的网格图,相邻格子之间有边权。 \(q\) 组询问求 \((a,b)\) 到 \((c,d)\) 的最短路。
\(m\leq10,n\leq10^5\)
思路

首先想利用网格图和这个 \(m=10\) 的条件,否则一定是朴素最短路没有优化空间的。注意到有 \(m=2\) 的部分分,联想到一道题堵塞的交通。这题是 \(m=2\) 的网格图维护连通性,做法是线段树。所以发现这个网格图应该也是满足分治可以用类似方法线段树维护的。
我们考虑维护列数,线段树 \([l,r]\) 维护第 \(l\) 列和第 \(r\) 列之间每一个 \((i,l)\) 到 \((j,r)\) 的最短路。所以我们希望维护一个 \(F(l,r,x,y)\) 表示  \((x,l)\) 到 \((y,r)\) 的最短路。发现如果考虑分治的话就可以:

\[F(l,r,x,y)=\min_k F(l,mid,x,k)+A_{k,mid}+F(mid+1,r,k,y)\]
其中 \(mid=\frac{l+r}{2}\),\(A_{k,mid}\) 表示 \((k,mid)\) 到 \((k,mid+1)\) 的边权。
于是我们就可以维护出任意一个 \(F(l,r,x,y)\) 了。之后考虑维护 \(F(i,i,x,y)\),即一列里面任意两点最短路。发现一定是在一列里面直接走或者从左边一列或右边一列加上列之间的边权的最小值。于是维护 \(L(i,x,y)\) 表示除了从 \((i,x)\) 出去和回到 \((i,y)\) 外其余的列数全部小于 \(i\) 的最短路。同理维护一个 \(R(i,x,y)\) 表示全部大于 \(i\) 的。
发现 \(L(i),R(i)\) 可以通过在 \(L(i-1),R(i+1)\) 的基础上新加上列之间的边权之后跑弗洛伊德得到。
之后再用 \(V(i)=\min(L(i),R(i))\) 再跑弗洛伊德。
最后时间复杂度可以做到 \(O(nm^3\log n+qm^2\log n)\)。
代码

[code]#include#define rep(i,a,b) for(register int i=(a);i>n>>Q;        rep(i,1,m){            rep(j,1,n-1){                cin>>A[j];            }        }        rep(i,1,m-1){            rep(j,1,n){                cin>>B[j];            }        }        memset(L,0x3f,sizeof L);        memset(R,0x3f,sizeof R);        rep(i,1,n){            memcpy(L,L[i-1],sizeof L);            if(i>1){                rep(j,1,m){                    rep(k,1,m){                        L[j][k]+=A[j][i-1]+A[k][i-1];                    }                }            }            rep(j,1,m){                L[j][j]=0;                L[j][j+1]=L[j+1][j]=min(L[j][j+1],ll(B[j]));            }            floyd(i,0);        }        for(int i=n;i>=1;--i){            memcpy(R,R[i+1],sizeof R);            if(i>a>>b>>p>>q;            if(b>q){                swap(a,p);                swap(b,q);            }            memset(ans,0x3f,sizeof ans);            ans[a]=0;            query(1,1,n,b,q);            cout
您需要登录后才可以回帖 登录 | 立即注册