题意:

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. Jmeter二次开发——自定义函数

    在之前的博文中,Jmeter二次开发--基于Java请求,已介绍了Jmeter二次开发的基础情况,上次分享的是java请求开发,今天来分享下Jmeter中的函数开发.聊到Jmeter的函数,知道Jme ...

  2. C语言中二维数组声明时,探究省略第一维的原因

    我们在使用二维数组作为参数时,我们既可以指明这个数组各个维度的维数,同时我们也可以省略一维,但是二维却不能省略.why呢?由于编译器原理的限制,在一个数组Elemtype test[m][n]中,访问 ...

  3. 解决 browser-sync start --server --files 文件不能同步的问题!

    解决 browser-sync start --server --files 文件不能同步的问题! 请看我的源命令: browser-sync start --server --file 'test2 ...

  4. widnows2008双网卡双ip不同Ip段

    机房内有不同段ip,因为线路不一样,比如普通带宽和cn2带宽,现有需求配置双网卡双ip ip1: 121.7*.*.*  255.255.255.192 121.7*.*129 ip2: 103.11 ...

  5. 超详细oracle 11g安装步骤 win版本

    1. 打开网址: https://edelivery.oracle.com 使用oracle 任意账号登录 账号:2696671285@qq.com 密码:Oracle123 感谢来自某位好心大佬的共 ...

  6. git的使用学习笔记3---关于项目分支创建克隆拉取推送

    一.创建项目 1.打开官网 2.填写相关内容 查看新创建的项目 3.选择方式 4.在git上新建文件夹 1)克隆: mkdir workspace 将代码克隆到本地,取本地配置的.ssh的文件 git ...

  7. Oracle 0至6级锁的通俗解释及实验案例_ITPUB博客 http://blog.itpub.net/30126024/viewspace-2156232/

    Oracle 0至6级锁的通俗解释及实验案例_ITPUB博客 http://blog.itpub.net/30126024/viewspace-2156232/

  8. 动态库与静态库的学习 博主写的很好 静态库 编译的时候 需要加上 static 动态库编译ok运行不成功就按照文章中的方法修改

    来源连接   http://www.cnblogs.com/skynet/p/3372855.html C++静态库与动态库 这次分享的宗旨是--让大家学会创建与使用静态库.动态库,知道静态库与动态库 ...

  9. luoguP2657 [SCOI2009] windy 数

    目录 luoguP2657 [SCOI2009] windy 数 简述题意: Solution: luoguP2657 [SCOI2009] windy 数 简述题意: 不含前导零且相邻两个数字之差至 ...

  10. OpenStack (cinder存储服务)

    cinder简介 提供 OpenStack 存储块(Volume)服务,该管理模块原来也为 Nova 的一部分,即 Nova-volume,后来从 Folsom 版本开始使用 Cinder 来分离出块 ...