H - 迷路 (矩阵快速幂, 拆点)

//https://vjudge.net/contest/521403#problem/H
#include<bits/stdc++.h>
#include<unordered_map>
#include<array>
#define ll long long
#define ull unsigned long long
#define all(a) a.begin(),a.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;

const int inf = 2e9 + 2;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-4;
const ll mod = 2009;
const int N = 2e3 + 5;

/*
写这题需要先解决一个子问题:
给你一个 n * n 的01矩阵g,如果a[i][j]代表有一条 i 到 j 的单向边,反之则没有。这个时候我问你从
一个起点 i 点走 k 步能走到 j 点方案数有多少?
如果我有一个 n * n 的矩阵,b[i][j] 代表从 i 点走 k - 1 步能走到 j 点的方案数。你会发现我拿这个矩阵乘
上最开始代表是否有边的01矩阵得到的新矩阵中 c[i][j] 代表的就是从 i 点走到 j 点走 k 步的方案数。
为什么?
我们得到的 c[i][j] = sum(b[i][mid] * a[mid][j])。这里的乘上 a[mid][i] 就相当于在 mid 点多走一步
走到 j 点。
所以我们只要把最开始的01矩阵乘上 k 次,得到的矩阵中的 c[st][ed] 就是最终答案。
那接下来还有两个问题,一个是 k 很大怎么办 ? 矩阵快速幂。
我的边权不为 1 怎么办 ? 拆点。
a 到 b 边长为 ,相当于 a 到 c ,c 到  d,d 到 b 各有一个边长为 1 的边。这样就能写成01矩阵的形式。因为这
里的 n 很小,边长也是在 9 以内的,所以矩阵很小,算上快速幂,时间复杂度完全ok。
*/

#define int ll
int n, T;

const int K = 110;
struct Matrix {
	ll a[K][K];
	Matrix() {}
	void clear() { memset(a, 0, sizeof a); }//变成0矩阵
	void init()//初始化为单位阵
	{
		clear();
		for (int i = 0; i < K; i++)
			a[i][i] = 1;
	}
	Matrix operator * (const Matrix& x) const
	{
		Matrix res;
		res.clear();
		for (int i = 0; i < K; i++)
			for (int j = 0; j < K; j++)
				for (int k = 0; k < K; k++)
					(res.a[i][j] += a[i][k] * x.a[k][j] % mod) %= mod;
		return res;
	}
};

Matrix qpow(Matrix base, int pow)
{
	Matrix ans;
	ans.init();
	while (pow)
	{
		if (pow & 1)
			ans = ans * base;
		pow /= 2;
		base = base * base;
	}
	return ans;
}

void solve()
{
	cin >> n >> T;

	Matrix ans;
	ans.clear();
	for (int i = 0; i < n; i++)
	{
		int st = i * 9;
		for (int j = 1; j <= 8; j++)
			ans.a[st + j - 1][st + j] = 1;
	}

	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			char ch;
			cin >> ch;
			int len = ch - '0';
			if (len)
				ans.a[i * 9 + len - 1][j * 9] = 1;
		}
	}

	ans = qpow(ans, T);
	cout << ans.a[0][(n - 1) * 9] << '\n';
}

signed main()
{
	IOS;
	int t = 1;
	//cin >> t;
	while (t--)
		solve();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值