【2019牛客暑期多校训练营(第二场)- E】MAZE(线段树优化dp,dp转矩阵乘法,线段树维护矩阵乘法)

题干:

链接:https://ac.nowcoder.com/acm/contest/882/E?&headNav=acm
来源:牛客网
 

Given a maze with N rows and M columns, where bijb_{ij}bij​ represents the cell on the i-row, j-th column. If bi,j="1"b_{i, j} = \texttt{"1"}bi,j​="1", it's a wall and can't not be passed. If you are on the cell bi,jb_{i, j}bi,j​, you can go to b(i+1),jb_{(i+1), j}b(i+1),j​, bi,(j−1)b_{i, (j-1)}bi,(j−1)​, or bi,(j+1)b_{i, (j+1)}bi,(j+1)​ as long as it's not a wall.

Sometime, a cell may be changed into wall, or vise versa. You need to find out the number of way to pass through the maze starting at some given cell and finishing at some given cell.

 

If the starting cell or finishing cell is a wall, there's clearly no way to pass through the maze.

Note that you can't go back to the cell you just from.

示例1

输入

复制

2 2 3
00
00
2 1 2
1 1 2
2 1 2

输出

复制

2
1

题目大意:

有一个 n*m 的 01 矩阵,1 表示不可行,0 代表可行;

每次可以从 (i, j) 走到 (i, j – 1),(i, j + 1) 和 (i + 1, j),且不能回到已走过的格子;

有 q 个以下两种操作:

1、将某个格子的状态反转,即1变0,0变1

2、询问从 (1, x) 走到 (n, y) 的方案数。

1 <= n,q <= 5e4,1 <= m <= 10。

解题报告:

先考虑不带修改的版本,设矩阵第 i 行第 j 个元素为 A[i][j];

dp[i][j] 表示从第 i – 1 行经过 (i – 1, j) 走到 (i, j) 的方案数,状态转移方程如下:

简单来说,dp[i][j] 等于 A[i – 1, j] 向左和向右 A[i – 1][k] 都等于 0 的那些 dp[i – 1][k] 的和;

如当 n = 2,m = 6 时:(最上面为第一行)

000100
101010

dp[2][2] = dp[1][1] + dp[1][2] + dp[1][3];

dp[2][4] = 0

dp[2][6] = dp[1][5] + dp[1][6]。

dp[2]的其余值都是0。

因此第 i 行的 dp 值到第 i + 1 行的 dp 值的转移可用矩阵 Mi 实现;

如上图第一行的 dp 值到第二行 dp 值的转移矩阵 M1:

(注意就算是a[i][j]==1,但是也要更新这一个值,但是其实并没有什么用,因为后面也会被continue掉)

111000
111000
111000
000000
000011
000011

那么要求从 (1, x) 走到 (n, y) 的方案数,即令 dp[1][x] = 1,求 dp[n + 1][y];

为什么是 dp[n + 1][y] 而不是 dp[n][y],因为 dp[n][y] 设定状态的时候,表示的就是从第 n – 1 行经过 (n – 1, y) 走到 (n, y) 的方案数,故意的漏掉那些从第 n 行走到 (n, y) 的方案数,这样做的目的也是便于直接统计,而 dp[n + 1][y] 则表示从第 n 行经过 (n, y) 的所有方案数,也就是我们要的答案了。

那么让矩阵M[i][j]代表的含义是从第i列走到第j列的方案数。

若令矩阵ans = M_1 \times M_2 \times M_3 \times ... \times M_n,则ans[x][y]就是答案。

再考虑带修改的版本;

因为每次都会修改一个点,也就是每次只会修改N个矩阵中的一个矩阵,然后再求矩阵的和。

因为矩阵乘法具有可交换性质,所以可以用线段树维护矩阵乘积:M_1 \times M_2 \times M_3 \times ... \times M_n,反转 (x, y) 的操作即重构 M_x,为单点修改,时间复杂度为 O(m^3logn);

根节点维护的就是 M_1 \times M_2 \times M_3 \times ... \times M_n,可 O(1) 回答询问,总时间复杂度 O(m^3logn)。

注意实现的细节,你如果在结构体中加上l和r节点的话,那矩阵最好重新定义一个结构体,不然的话你pushup就要多写两行。如果不多这几行的话显然是不对的。(因为你的l和r就被冲掉了)

再贴一个别人的题解:

这样的话就可以理解为,对于每一个查询只有dp[1][x]是1,dp[1]的其他值都是0,然后求dp[n+1][y],

所以就是=(0,0,,,第x位上是1,,0) * 构造的矩阵的第四列,也就是构造的矩阵的M[x][y]项。

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define FF first
#define SS second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair<int,int> PII;
const int MAX = 50005 + 5;
const ll mod = 1e9 + 7;
int n,m,q;
struct TREE {int l,r;ll m[12][12];
} tr[MAX<<2];
int a[MAX][13];
TREE mul(TREE a,TREE b) {TREE c;for(int i = 1; i<=m; i++) {for(int j = 1; j<=m; j++) {c.m[i][j] = 0;for(int k = 1; k<=m; k++) {c.m[i][j] = (c.m[i][j] + (a.m[i][k]*b.m[k][j])%mod) % mod;}}}return c;
} 
void modify(int cur,int x) {memset(tr[cur].m,0,sizeof tr[cur].m);for(int i = 1; i<=m; i++) {if(a[x][i] == 1) continue;tr[cur].m[i][i]=1;for(int j = i-1; j>=1&&a[x][j]==0; j--) tr[cur].m[i][j] = 1;for(int j = i+1; j<=m&&a[x][j]==0; j++) tr[cur].m[i][j] = 1;}
}void pushup(int cur) {int l = tr[cur].l,r = tr[cur].r;tr[cur] = mul(tr[cur*2],tr[cur*2+1]);tr[cur].l = l,tr[cur].r = r;
}
void build(int l,int r,int cur) {tr[cur].l = l,tr[cur].r = r;if(l == r) {modify(cur,l);return;}int m = (l+r)>>1;build(l,m,cur*2);build(m+1,r,cur*2+1);pushup(cur); 
}
void update(int tar,int cur) {if(tr[cur].l == tr[cur].r) {modify(cur,tr[cur].l); return;}int mid = (tr[cur].l + tr[cur].r)>>1;if(tar <= mid) update(tar,cur*2);else update(tar,cur*2+1);pushup(cur); 
}
int main()
{cin>>n>>m>>q;for(int i = 1; i<=n; i++) {for(int j = 1; j<=m; j++) scanf("%1d",&a[i][j]);}build(1,n,1);while(q--) {int op,x,y;scanf("%d%d%d",&op,&x,&y);if(op == 1) a[x][y] ^= 1,update(x,1);else printf("%lld\n",tr[1].m[x][y]%mod);}return 0 ;
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/439899.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Apollo进阶课程⑰丨Apollo感知之旅——传感器选择和安装

目录 1.激光雷达 2.相机 3.Radar毫米波 4.安装传感器 原文链接&#xff1a;进阶课程⑰丨Apollo感知之旅——传感器选择和安装 上周阿波君为大家详细介绍了「进阶课程⑯ Apollo感知之旅——感知概况」。 传感器是一种检测装置&#xff0c;能感受到被测量的信息&#xff0c;…

2.2)深度学习笔记:优化算法

目录 1&#xff09;Mini-batch gradient descent&#xff08;重点&#xff09; 2&#xff09;Understanding mini-batch gradient descent 3&#xff09;Exponentially weighted averages 4&#xff09;Understanding exponetially weighted averages 5&#xff09;Bias c…

Apollo进阶课程⑱丨Apollo感知之旅——传感器标定

目录 传感器标定 标定的目的 传感器标定算法 标定案例解析 3D标定间制作 Cmaera-to-Camera外参标定 Lidar-to-Camera外参标定 Lidar-to-Lidar外参标定 Lidar内参标定 Lidar-to-GPS外参标定 自然场景的Lidar-to-Camera外参标定 自然场景的Bifocal Camera外参标定 C…

一步步编写操作系统 15 CPU与外设通信——IO接口,下

既然都说到IO接口了&#xff0c;不知道各位有没有疑问&#xff0c;cpu是怎样访问到IO接口呢&#xff1f;肯定得有个链路吧&#xff1f;什么&#xff1f;有隐约听到有同学开玩笑说&#xff1a;cpu用无线访问其它设备。哈哈&#xff0c;不知道各位听说过没有&#xff0c;无线的终…

Telnet端口连接Linux服务器失败

在ubuntu写了个服务器端口号是666 &#xff0c;ip地址是192.168.96.129 在windows用telnet无法连接上 首先检查windows telnet服务是否打开 Windows 10操作系统上使用telnet命令&#xff08;图文&#xff09;_时间-CSDN博客_windows使用telnet命令 测试网络是否通&#xff1a;…

重磅 | 完备的 AI 学习路线,最详细的资源整理!

本文转自微信公众号&#xff1a;Datawhale&#xff08;强烈推荐&#xff09; 原创&#xff1a; AIUnion Datawhale 今天 【导读】 本文由知名开源平台&#xff0c;AI技术平台以及领域专家&#xff1a;Datawhale&#xff0c;ApacheCN&#xff0c;AI有道和黄海广博士联合整理贡献…

Windows/Linux 下使用telnet发送消息

Windows下使用telnet 1.首先打开cmd命令行连接上服务器端口 连不上可以参考这篇 Telnet端口连接Linux服务器失败_m0_46480482的博客-CSDN博客 telnnt <ip地址> <端口号> 2. 连接成功后&#xff0c;会发现是一片黑的 按住 ctrl ] 可以招出提示 输入 &#x…

Apollo进阶课程⑲丨Apollo感知之旅——感知算法

目录 点云感知 启发式方法&#xff1a;NCut 深度学习方法&#xff1a;CNNSeg 视觉感知 CNN检测 CNN分割 后处理 红绿灯感知 基于深度学习的红绿灯感知模块 Radar感知 超声波感知 原文链接&#xff1a;进阶课程⑲丨Apollo感知之旅——感知算法 感知是自动驾驶的第一环…

动手学PaddlePaddle(0):新版本PaddlePaddle安装

目录 0.引言 1.环境 2.Windows下安装 安装Python 安装PaddlePaddle 0.引言 今天介绍如何安装新版本的PaddlePaddle&#xff0c;现在最新版的PaddlePaddle是指Fluid版&#xff0c;Fluid可以让用户像Pytorch和TensorFlow Eager Execution一样执行程序&#xff0c;也就是说P…

一步步编写操作系统 18 操作显卡,显存,显示器 下

接上回&#xff0c;大家看下显卡各种模式的内存分布。 各外部设备都是通过软件指令的形式与上层接口通信的&#xff0c;显卡&#xff08;显示适配器&#xff09;也不例外&#xff0c;所以它也有自己的bios。位置是0xC0000到0xC7FFF。显卡支持三种模式&#xff0c;文本模式、黑白…

VMware 安装VMware Tools

想要在linux和windows之间复制粘贴&#xff0c;把之前一直没有下的vmwaretools的下载过程记录一下。 1.左上角菜单 ->虚拟机 ->安装 vmware tools (我已经点过了所以是取消安装) 2.桌面多了一个VMware tools &#xff0c;点进去看一下位置&#xff0c;复制一下tar.gz的文…

Apollo进阶课程⑳丨Apollo感知之旅——机器学习与感知的未来

目录 1机器学习 可解释性是否需要 其它算法 2感知的未来 Sensor迭代 深度学习仿真数据AI芯片 智能交通设施 3思考 原文链接&#xff1a;进阶课程⑳丨Apollo感知之旅——机器学习与感知的未来 自动驾驶感知中的机器学习最大问题在于系统对模块的要求与普通的机器学习不同…

一步步编写操作系统 19 改进MBR,直接操作显卡

到目前为止&#xff0c;说了一部分有关显存的内容&#xff0c;这对于一般的输出来说已经足够了&#xff0c;下面咱们可以尝试写显存啦。我们将之前MBR改造一下&#xff0c;保留滚屏的操作&#xff0c;只修改有关输出的部分。即把通过bios的输出改为通过显存&#xff0c;你会发现…

Apollo进阶课程㉑丨Apollo规划技术详解——Basic Motion Planning and Overview

原文链接&#xff1a;进阶课程㉑丨Apollo规划技术详解——Basic Motion Planning and Overview 运动规划&#xff08;Motion Planning&#xff09;就是在给定的位置A与位置B之间为机器人找到一条符合约束条件的路径。这个约束可以是无碰撞、路径最短、机械功最小等。具体的案例…

ROS机器人导航仿真(kinetic版本)

准备工作&#xff1a; ubuntu 16.04系统;ROS kinetic版本;ROS包turtlebot,导航包rbx1,模拟器arbotix&#xff0c;可视化rviz 1、安装ubuntu 16.04系统与安装ROS kinetic版本自行百度安装。一下链接可作为参考。 http://blog.csdn.net/weicao1990/article/details/52575314 2…

1.深度学习练习:Python Basics with Numpy(选修)

本文节选自吴恩达老师《深度学习专项课程》编程作业&#xff0c;在此表示感谢。 课程链接&#xff1a;https://www.deeplearning.ai/deep-learning-specialization 目录 1 - Building basic functions with numpy 1.1 - np.exp(), sigmoid function 1.2 - Sigmoid gradient …

一步步编写操作系统 20 x86虚拟bochs一般用法 上

bochs一般用法 bochs是一个开源x86 虚拟机软件。在它的实现中定义了各种数据结构来模拟硬件&#xff0c;用软件模拟硬件缺点是速度比较慢&#xff0c;毕竟全是软件来模拟&#xff0c;您想&#xff0c;虚拟机还要在软件中模拟各种中断&#xff0c;能不慢吗。不过它的功能非常强…

2.3)深度学习笔记:超参数调试、Batch正则化和程序框架

目录 1&#xff09;Tuning Process 2&#xff09;Using an appropriate scale to pick hyperparameters 3&#xff09;Hyperparameters tuning in practice: Pandas vs. Caviar 4&#xff09;Normalizing activations in a network&#xff08;重点&#xff09; 5&#xf…

2.深度学习练习:Logistic Regression with a Neural Network mindset

本文节选自吴恩达老师《深度学习专项课程》编程作业&#xff0c;在此表示感谢。 课程链接&#xff1a;https://www.deeplearning.ai/deep-learning-specialization/ You will learn to: Build the general architecture of a learning algorithm, including: Initializing para…

JVM内存区域详解

Java中虚拟机在执行Java程序的过程中会将它所管理的内存区域划分为若干不同的数据区域。下面来介绍几个运行时数据区域。 一、程序计数器 1.1 简述 程序计数器&#xff08;Program Counter Register&#xff09;是一块较小的内存空间&#xff0c;它的作用可以看做是当前线程所…