【模拟赛】纪中提高A组 19.8.12 测试 迷宫

Posted on 2019-08-12 19:44  opethrax  阅读(256)  评论(0编辑  收藏  举报

# 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;
}