1. 程式人生 > >【JZOJ5223】B【矩陣乘法】【DFS】

【JZOJ5223】B【矩陣乘法】【DFS】

題目大意:

題目連結:https://jzoj.net/senior/#main/show/5223
題目圖片:
http://wx4.sinaimg.cn/mw690/0060lm7Tly1fy2w5kn4jbj30in0gk74z.jpg
給定一個 3 × 3 3\times3 的網格圖,一開始每個格子上都站著一個機器人。每一步機器人可以走到相鄰格子或留在原地,同一個格子上可以有多個機器人。問走 n

n 步後,有多少種走法,滿足每個格子上都有機器人。答案對 1 0 9 + 7 10^9+7
取模。


思路:

n 1 0 18 n\leq 10^{18} 告訴我們什麼?
l

o g   n ? log\ n? 顯然不可做。
n ? \sqrt{n}? 顯然過不了。
3 × 3 3\times 3 的矩陣?矩陣乘法!

f [ i ] [ j ] f[i][j] 表示走 n n 步從格子 i i 到格子 j j 的方案數。那麼很明顯,中間矩陣 a a 就是相鄰格子為 1 1 ,否則為 0 0 。然後列舉每一個起始格子,跑一邊矩陣乘法。就可以得到任意兩點走 n n 步的方案數。
然後就 D F S DFS 每一個格子的機器人最終會走到哪一個格子。然後求 a n s ans 即可。


程式碼:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;

const int MOD=1000000007;
ll n,f[10][10],a[10][10],ans;
bool used[10];
const ll A[10][10]=
{
	{0,0,0,0,0,0,0,0,0,0},
	{0,1,1,0,1,0,0,0,0,0},
	{0,1,1,1,0,1,0,0,0,0},
	{0,0,1,1,0,0,1,0,0,0},
	{0,1,0,0,1,1,0,1,0,0},
	{0,0,1,0,1,1,1,0,1,0},
	{0,0,0,1,0,1,1,0,0,1},
	{0,0,0,0,1,0,0,1,1,0},
	{0,0,0,0,0,1,0,1,1,1},
	{0,0,0,0,0,0,1,0,1,1}
};

void mul(int x)
{
	ll c[10];
	memset(c,0,sizeof(c));
	for (int i=1;i<=9;i++)
		for (int j=1;j<=9;j++)
			c[j]=(c[j]+(f[x][i]*a[i][j]))%MOD;
	memcpy(f[x],c,sizeof(f[x]));
}

void mulself()
{
	ll c[10][10];
	memset(c,0,sizeof(c));
	for (int i=1;i<=9;i++)
		for (int j=1;j<=9;j++)
			for (int k=1;k<=9;k++)
				c[i][j]=(c[i][j]+a[i][k]*a[k][j])%MOD;
	memcpy(a,c,sizeof(a));
}

void ksm(ll m,int x)
{
	while (m)
	{
		if (m&1) mul(x);
		mulself();
		m>>=1;
	}
}

void dfs(int x,ll s)
{
	if (x>9)
	{
		ans=(ans+s)%MOD;
		return;
	}
	for (int i=1;i<=9;i++)
		if (!used[i])
		{
			used[i]=1;
			dfs(x+1,s*f[x][i]%MOD);
			used[i]=0;
		}
}

int main()
{
	cin>>n;
	for (int i=1;i<=9;i++)
	{
		f[i][i]=1;
		memcpy(a,A,sizeof(a));
		ksm(n,i);
	}
	dfs(1,1);
	cout<<ans;
	return 0;
}