# Task.1 迷宫
本来是整场比赛的题解的,但是另外两道题到现在都没搞完...
题目大意:给出一个 \(N\times M\) 的网格状的迷宫,每个格子可能存在障碍无法通行或者可以通行,小奇被困在迷宫的某个格子中,迷宫的出口在小奇的右边,小奇只能往上、右、下方向移动。小奇的位置和迷宫的出口以及迷宫的构造会发生改变。现在给出 \(Q\) 次操作,\(opt=1\) 改变迷宫坐标为 \((a,b)\) 的格子的状态(可通行->不可通行,不可通行->可通行),\(opt=2\) 给出小奇的位置 \((a,b)\) 和迷宫的出口 \((c,d)\),询问小奇能否到达出口,若能输出最短距离,否则输出 "\(-1\)"。
数据范围:\(1\leq N\leq 5,1\leq M\leq 2\cdot 10^5,1\leq Q\leq 5\cdot 10^4\)
考场上没莽出来,想到了一维的情况可以线段树瞎搞。
实际上解法确实是线段树。看到这么小的 \(N\),估摸着是状压之类的东西,但是不行。
\(N=1\) 的情况,我们只要维护一棵线段树,节点 \([l,r]\) 存一个值 \(dis\),表示两个端点间的距离,合并时直接把 \([l,mid]\) 和 \([mid+1,r]\) 的 \(dis\) 加起来就得到了 \([l,r]\) 端点的距离。查询操作直接查询区间 \([l,r]\) 即可,修改时把所有包含被修改的点的区间都进行修改。
现在看 \(N=2\) 的情况,线段树的节点需要发生些小变化,区间 \([l,r]\) 每个位置有两个格子,左右端点的位置可以两两走到或者走不到,所以节点存四个值,分别表示左上到右下,左下到右下,左上到右上,左下到右上的最短路径。合并时枚举跨过区间中点的那一步怎么走即可。查询修改和一维的一样。
考虑 \(N\) 更大的情况,对于区间 \([l,r]\),我们的走法更多了,\(l\) 处有位置 \((i,l)\),\(r\) 处有位置 \((j,r)\),可能存在 \((i,l)\) 走到 \((j,r)\) 的最短距离 \(f_{i,j}\),有 \(N\times N\) 种情况,存储在一个矩阵中。合并时类似 \(Floyd\),依然枚举跨过中点的那一步\((k)\)怎么走、经过了哪个点,如下图:
在查询时,查询区间 \([b,d]\) 所对应的矩阵中 \(f_{a,c}\) 的值。修改时重构节点 \([b,b]\),再更新所有包含它的节点。
综上所述,我们维护一棵树上节点是一个类似 \(Floyd\) 的最短路的矩阵,矩阵合并 \(O(N^3)\),总复杂度 \(O(\ (M+Q) log M N^3\ )\)。
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
template<class T>void read(T &x){
x=0; char c=getchar();
while(c<'0'||'9'<c)c=getchar();
while('0'<=c&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
}
typedef pair<int,int> pr;
const int N=200050,Q=50050;
int n,m,q;
int e[7][N];
void cmin(int &x,int y){if(x>y)x=y;}
struct node{
int f[6][6];
void init(int x){
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++)if(e[i][x]==1)f[i][i]=0;
for(int len=2;len<=n;len++)
for(int i=1,j=i+len-1;j<=n;i++,j++)
cmin(f[i][j],f[i][j-1]+f[j][j]+1);
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)
f[i][j]=f[j][i];
}
void merge(node u,node v){
node res;
memset(res.f,0x3f,sizeof(res.f));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++)
cmin(res.f[i][j],u.f[i][k]+v.f[k][j]+1);
f[i][j]=res.f[i][j];
}
}
void out(){
for(int i=1;i<=n;i++,puts(""))
for(int j=1;j<=n;j++)printf("%d ",f[i][j]);
}
};
struct segt{
#define lson l,mid,p<<1
#define rson mid+1,r,p<<1|1
node s[N<<2];
void pushup(int p){
s[p].merge(s[p<<1],s[p<<1|1]);
}
void build(int l,int r,int p){
if(l==r){s[p].init(l); return ;}
int mid=(l+r)>>1;
build(lson); build(rson); pushup(p);
}
void upd(int x,int l,int r,int p){
if(l==r){s[p].init(l); return ;}
int mid=(l+r)>>1;
if(x<=mid) upd(x,lson); else upd(x,rson); pushup(p);
}
node query(int L,int R,int l,int r,int p){
if(L<=l&&r<=R) return s[p];
int mid=(l+r)>>1; node res;
if(L<=mid&&R<=mid) res=query(L,R,lson);
else if(mid<L) res=query(L,R,rson);
else res.merge(query(L,R,lson),query(L,R,rson));
return res;
}
}t;
int main(){
// freopen("maze.in","r",stdin);
// freopen("maze.out","w",stdout);
read(n); read(m); read(q);
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) read(e[i][j]);
t.build(1,m,1);
node res;
int op,a,b,c,d;
for(int i=1;i<=q;i++){
read(op); read(a); read(b);
if(op==1){e[a][b]^=1; t.upd(b,1,m,1);}
else {
read(c); read(d);
if(b>d){puts("-1"); continue;}
res=t.query(b,d,1,m,1);
if(res.f[a][c]!=0x3f3f3f3f) printf("%d\n",res.f[a][c]);
else puts("-1");
}
}
return 0;
}