牛客第二场 E MAZE —— 线段树 + 矩阵

本文介绍了一种结合矩阵动态规划(matrix DP)与线段树(segment tree)的算法,用于解决一类路径计数问题,特别是在二维网格中进行多次查询与更新的场景下。通过将相邻行的状态转移视为矩阵乘法,利用线段树高效地维护状态转移矩阵,实现了O(qm^3logn)的时间复杂度。

题目链接:点我啊╭(╯^╰)╮

题目大意:

    一张 n × m n×m n×m 的图, 0 0 0 表示可走, 1 1 1 不可走
    只能向下、左右走,不能回走
    多次查询,两种操作:
    将某个位置反转
    问从第一行 a i a_i ai 到第 n n n b i b_i bi 的的方案数

解题思路:

     d p [ i ] [ j ] = s u m ( d p [ i − 1 ] [ k ] dp[i][j] = sum(dp[i-1][k] dp[i][j]=sum(dp[i1][k] for ( k < j (k < j (k<j and b i k = b i k + 1 = . . . = b i j = 0 ) ) + b_{ik}=b_{ik+1}=...=b_{ij}=0))+ bik=bik+1=...=bij=0))+
                        s u m ( d p [ i − 1 ] [ k ] sum(dp[i-1][k] sum(dp[i1][k] for ( k > j (k > j (k>j and b i k = b i k − 1 = . . . = b i j = 0 ) ) b_{ik}=b_{ik-1}=...=b_{ij}=0)) bik=bik1=...=bij=0))

    所以相邻两行就可以用矩阵相乘计算
    然后用线段树维护
    每个节点表示一段区间的系数转移矩阵
    时间复杂度: O ( q m 3 l o g n ) O(qm^3logn) O(qm3logn)
    详细可看:这里

核心:线段树维护系数矩阵

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const ll mod = 1e9 + 7;
const int maxn = 5e4 + 5;
int n, m, q, op;
int b[maxn][15];
struct node{
	ll s[15][15];
	void init() {memset(s, 0, sizeof(s));}
	node operator + (const node &a) const {
		node ret; ret.init();
		for(int i=1; i<=m; i++)
			for(int j=1; j<=m; j++)
				for(int k=1; k<=m; k++)
					ret.s[i][j] = (ret.s[i][j]+s[i][k]*a.s[k][j]%mod) % mod;
		return ret;
	}
} t[maxn<<2];  

void get(node &h, int r){
	h.init();
	for(int i=1; i<=m; i++)
		if(!b[r][i]){
			h.s[i][i] = 1;
			for(int j=i-1; j && !b[r][j]; j--) h.s[j][i] = 1;
			for(int j=i+1; j<=m && !b[r][j]; j++) h.s[j][i] = 1;
		}
}

void build(int l, int r, int rt){
	if(l == r){
		get(t[rt], l);
		return;
	}
	int m = l + r >> 1;
	build(l, m, rt<<1);
	build(m+1, r, rt<<1|1);
	t[rt] = t[rt<<1] + t[rt<<1|1];
}
void update(int pos, int l, int r, int rt){
	if(l>pos || r<pos) return;
	if(l == r){
		get(t[rt], l);
		return;
	}
	int m = l + r >> 1;
	update(pos, l, m, rt<<1);
	update(pos, m+1, r, rt<<1|1);
	t[rt] = t[rt<<1] + t[rt<<1|1];
}
int main() {
	scanf("%d%d%d", &n, &m, &q);
	for(int i=1; i<=n; i++){
		char s[15];
		scanf("%s", s+1);
		for(int j=1; j<=m; j++)
			if(s[j]=='1') b[i][j] = 1;
	}
	build(1, n, 1);
	while(q--){
		int u, v;
		scanf("%d%d%d", &op, &u, &v);
		if(op & 1) b[u][v] ^= 1, update(u, 1, n, 1);
		else printf("%d\n", t[1].s[u][v]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值