题意:

n * m的矩阵,为0表示可以走,1不可以走。规定每走一步只能向下、向左、向右走。现给定两种操作:

一.1 x y表示翻转坐标(x,y)的0、1。

二.2 x y表示从(1,x)走到(n,y)有几种走法

思路:

假设\(dp[i][j]\)表示从下一层能到达(i,j)点的路径数,那么显然到达(i,j)的路径数为\(dp[i + 1][j]\)。

我们能很显然的得到转移方程\(dp[i][j] = \sum_{k = l}^r dp[i - 1][k]\),其中l~r为(i,j)下方能直接走到(i,j)的连续区间。

我们可以直接用矩阵维护这个转移方程:

\[\left(
\begin{matrix}
dp[i][1] & dp[i][2] & dp[i][3] & \cdots & dp[i][m]
\end{matrix}
\right)
*
A_i=
\left(
\begin{matrix}
dp[i + 1][1] & dp[i + 1][2] & dp[i + 1][3] & \cdots & dp[i + 1][m]
\end{matrix}
\right)
\]

然后用线段树维护矩阵乘积即可

从(1,x)走到(n,y)只需把x位置置为1,然后乘以\(\prod_{i = 1}^n A_i\)

代码:

#include<cstdio>
#include<set>
#include<cmath>
#include<stack>
#include<vector>
#include<queue>
#include<cstring>
#include<string>
#include<sstream>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 50000 + 5;
const int INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
int n, m, q;
int mp[maxn][12];
char s[12];
struct Mat{
ll s[12][12];
void init_zero(){
for(int i = 0; i < 12; i++)
for(int j = 0; j < 12; j++)
s[i][j] = 0;
}
};
Mat pmul(Mat a, Mat b, int len){
Mat c;
c.init_zero();
for(int i = 1; i <= len; i++){
for(int j = 1; j <= len; j++){
for(int k = 1; k <= len; k++){
c.s[i][j] = (c.s[i][j] + a.s[i][k] * b.s[k][j]) % MOD;
}
}
}
return c;
} Mat mul[maxn << 2], a[maxn];
void pushup(int rt){
mul[rt] = pmul(mul[rt << 1], mul[rt << 1 | 1], m);
}
void build(int l, int r, int rt){
if(l == r){
for(int i = 1; i <= m; i++)
for(int j = 1; j <= m; j++)
mul[rt].s[i][j] = a[l].s[i][j];
return;
}
int m = (l + r) >> 1;
build(l, m, rt << 1);
build(m + 1, r, rt << 1 | 1);
pushup(rt);
}
void update(int pos, int l, int r, Mat aa, int rt){
if(l == r){
for(int i = 1; i <= m; i++)
for(int j = 1; j <= m; j++)
mul[rt].s[i][j] = aa.s[i][j];
return;
}
int m = (l + r) >> 1;
if(pos <= m)
update(pos, l, m, aa, rt << 1);
else
update(pos, m + 1, r, aa, rt << 1 | 1);
pushup(rt);
}
int main(){
scanf("%d%d%d", &n, &m, &q);
for(int i = 1; i <= n; i++){
scanf("%s", s + 1);
for(int j = 1; j <= m; j++){
mp[i][j] = s[j] - '0';
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
int base;
base = 1;
for(int k = j; k >= 1; k--){
if(mp[i][k] == 1) base = 0;
a[i].s[k][j] = base;
}
base = 1;
for(int k = j; k <= m; k++){
if(mp[i][k] == 1) base = 0;
a[i].s[k][j] = base;
}
}
} // for(int k = 1; k <= n; k++){
// for(int i = 1; i <= m; i++){
// for(int j = 1; j <= m; j++){
// printf("%d ", a[k].s[i][j]);
// }
// puts("");
// }
// puts("*****");
// } build(1, n, 1);
while(q--){
int ques, i, j;
scanf("%d", &ques);
scanf("%d%d", &i, &j);
if(ques == 1){
mp[i][j] = !mp[i][j];
for(int j = 1; j <= m; j++){
int base;
base = 1;
for(int k = j; k >= 1; k--){
if(mp[i][k] == 1) base = 0;
a[i].s[k][j] = base;
}
base = 1;
for(int k = j; k <= m; k++){
if(mp[i][k] == 1) base = 0;
a[i].s[k][j] = base;
}
}
update(i, 1, n, a[i], 1);
}
else{
Mat ret;
ret.init_zero();
ret.s[1][i] = 1;
ret = pmul(ret, mul[1], m);
printf("%lld\n", ret.s[1][j]);
}
}
return 0;
}
/*
2 6 1
0 0 0 1 0 0
1 0 1 0 1 0
*/

2019牛客多校第二场E MAZE(线段树 + 矩阵)题解的更多相关文章

  1. MAZE(2019年牛客多校第二场E题+线段树+矩阵乘法)

    题目链接 传送门 题意 在一张\(n\times m\)的矩阵里面,你每次可以往左右和下三个方向移动(不能回到上一次所在的格子),\(1\)表示这个位置是墙,\(0\)为空地. 现在有\(q\)次操作 ...

  2. [2019牛客多校第二场][E. MAZE]

    题目链接:https://ac.nowcoder.com/acm/contest/882/E 题目大意:有一个\(n\times m\)的01矩阵,一开始可以从第一行的一个点出发,每次可以向左.向右. ...

  3. 2019牛客多校第二场 A Eddy Walker(概率推公式)

    2019牛客多校第二场 A Eddy Walker(概率推公式) 传送门:https://ac.nowcoder.com/acm/contest/882/A 题意: 给你一个长度为n的环,标号从0~n ...

  4. 2019牛客多校第一场E ABBA(DP)题解

    链接:https://ac.nowcoder.com/acm/contest/881/E 来源:牛客网 ABBA 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 524288K,其他语 ...

  5. 2019牛客多校第二场H-Second Large Rectangle

    Second Large Rectangle 题目传送门 解题思路 先求出每个点上的高,再利用单调栈分别求出每个点左右两边第一个高小于自己的位置,从而而得出最后一个大于等于自己的位置,进而求出自己的位 ...

  6. [2019牛客多校第二场][G. Polygons]

    题目链接:https://ac.nowcoder.com/acm/contest/882/G 题目大意:有\(n\)条直线将平面分成若干个区域,要求处理\(m\)次询问:求第\(q\)大的区域面积.保 ...

  7. 2019 牛客多校第二场 H Second Large Rectangle

    题目链接:https://ac.nowcoder.com/acm/contest/882/H 题目大意 给定一个 n * m 的 01 矩阵,求其中第二大的子矩阵,子矩阵元素必须全部为 1.输出其大小 ...

  8. 2019牛客多校第二场H题(悬线法)

    把以前的题补补,用悬线求面积第二大的子矩形.我们先求出最大子矩阵的面积,并记录其行三个方向上的悬线长度.然后排除这个矩形,记得还得特判少一行或者少一列的情况 #include <bits/std ...

  9. 2019牛客多校第二场D-Kth Minimum Clique

    Kth Minimum Clique 题目传送门 解题思路 我们可以从没有点开始,把点一个一个放进去,先把放入一个点的情况都存进按照权值排序的优先队列,每次在新出队的集合里增加一个新的点,为了避免重复 ...

随机推荐

  1. Python设计模式面向对象编程

    前言   本篇文章是基于极客时间王争的<设计模式之美>做的总结和自己的理解.  说到面向对象编程,作为一个合格的Pythoner,可以说信手拈来.毕竟在Python里"万物都是对 ...

  2. Centos7 添加用户及设置权限

    一.添加用户 1.登录root 用户 [gau@localhost /]$ su Password: # 输入密码 [root@localhost /]# 2.添加用户 [root@localhost ...

  3. Lambda架构正是这样一种用来处理不能够直接实时计算问题的通用架构

    https://mp.weixin.qq.com/s/BGHOw12iCASJy1pgkYZi3w 当数据处理做不到实时,应该怎么办?

  4. 熟悉而陌生API:Promise

    前言 ES6 发布到现在差不多有5年时间了.在这5年时间里ES6摧枯拉朽般的将现代前端"改朝换代",Promise是其中"大将"般的存在,影响着无数的前端库和A ...

  5. IntelliJ Idea 解决 Could not autowire. No beans of 'xxxx' type found 的错误提示

    IntelliJ Idea 解决 Could not autowire. No beans of 'xxxx' type found 的错误提示哈,在使用 @Autowired 时,今天又遇一坑,这俩 ...

  6. Uniter 单一框架学习

    unittest单元测试框架不仅可以适用于单元测试,还可以适用WEB自动化测试用例的开发与执行,该测试框架可组织执行测试用例,并且提供了丰富的断言方法,判断测试用例是否通过,最终生成测试结果. 一.u ...

  7. Quartz.Net 组件的封装使用Quartz.AspNetCore

    Quartz.Net 组件的封装使用 Quartz.Net是面向.NET的一款功能齐全的开源作业调度组件,你可以把它嵌入你的系统中实现作业调度,也可以基于Quartz.Net开发一套完整的作业调度系统 ...

  8. 统一资源定位符 (URL)

    统一资源标识符(uniform/universal resource identifier,URI)用于表示Internet中的资源(通常是文档).URI 主要用于两种目的,其一是命名资源,尽管此时把 ...

  9. 周期性清除Spark Streaming流状态的方法

    在Spark Streaming程序中,若需要使用有状态的流来统计一些累积性的指标,比如各个商品的PV.简单的代码描述如下,使用mapWithState()算子: val productPvStrea ...

  10. Spring Boot 两种多数据源配置:JdbcTemplate、Spring-data-jpa

    多数据源配置 JdbcTemplate支持 Spring-data-jpa支持 多数据源配置 创建一个Spring配置类,定义两个DataSource用来读取application.propertie ...