牛客多校赛第二场E
dp + 线段树 维护矩阵
大概题意是给出01矩阵,0代表可走1代表不可走,问从(1,x) 到(n,y)有多少走法,为什么要有线段树呢,因为题目给出了一个操作代表可以把(x,y)的状态反过来,0是1,1变为0;
那么要线段树的作用是什么.维护矩阵,矩阵如何得来.
我们知道我们可以从一个点向左右下走,那么 (i + 1,j)这个点有多少个在i行的点可以到呢
假如i行状态为100010 到达 (i+ 1 ,2) 可以由(i ,1)(i , 2)(i ,3)这3个点可以到达 那么是不是就可以知道
dp[i + 1][2] = dp[i][1] + dp[i][2] + dp[i][3] ,对于每个点来说我们都可以这么推出来那么
仔细想一想是不是
所以我们用线段树维护出每一行的状态转移矩阵将每一行的状态转移矩阵乘起来就是从第一行到最后一行的最后的状态,最后输出
A(x,y)这里为什么需要直接输出Ax,y 我还没太想明白 后补把。。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 50000 + 5;
int n,m,q;
struct Mat {
long long m[15][15];
}tree[N << 2];
Mat operator *(Mat a,Mat b)
{
Mat c;
memset(c.m,0,sizeof(c.m));
for(int k = 0; k < m;k++){
for(int i = 0; i < m; i++){
if(a.m[i][k] == 0) continue;
for(int j = 0; j < m; j++)
c.m[i][j] = (c.m[i][j]+a.m[i][k]*b.m[k][j])%1000000007;
}
}
return c;
}
string s[N];
void chushi(int l , int rt){
memset(tree[rt].m , 0, sizeof tree[rt].m);
for(int i = 0; i < m; i ++){
for(int j = i; j >= 0; j --){
if(s[l][j] == '0') tree[rt].m[j][i] = 1;
else break;
}
for(int j = i; j < m; j ++){
if(s[l][j] == '0'){
tree[rt].m[j][i] = 1;
}
else break;
}
}
}
void build(int l, int r ,int rt){
if(l == r){
chushi(l ,rt);
return ;
}
int mid = (l + r) >> 1;
build(l , mid , rt <<1 );
build(mid + 1, r , rt << 1| 1);
tree[rt] = tree[rt <<1]* tree[rt <<1|1];
}
void update(int pos, int l ,int r ,int rt){
if(l == r){
chushi(l , rt);
return ;
}
int mid = (l + r) >> 1;
if(pos <= mid) update(pos, l , mid, rt <<1);
else update(pos, mid + 1, r , rt << 1|1);
tree[rt] = tree[rt <<1]* tree[rt <<1|1];
}
int main()
{
cin >> n >> m >> q;
for(int i = 1; i <= n; i ++){
cin >> s[i];
}
build(1,n,1);
for(int i = 1; i <= q; i ++){
int op, x,y;
cin >> op >>x >> y;
if(op == 1){
if(s[x][y - 1] == '1') s[x][y - 1] = '0';
else s[x][y - 1] = '1';
update(x, 1 , n , 1);
}
else{
cout << tree[1].m[x - 1][y - 1] <<endl;
}
}
}