1. 程式人生 > >【bzoj1297】[SCOI2009]迷路 矩陣乘法

【bzoj1297】[SCOI2009]迷路 矩陣乘法

時間復雜度 brush 時間 col 表示 data 接下來 con pan

題目描述

給出一個 $n$ 個點的有向圖,每條邊的權值都在 $[1,9]$ 之間。給出 $t$ ,求從 $1$ 到 $n$ ,經過路徑邊權和恰好為 $t$ 的方案數模2009。

輸入

第一行包含兩個整數,N T。 接下來有 N 行,每行一個長度為 N 的字符串。 第i行第j列為‘0‘表示從節點i到節點j沒有邊。 為‘1‘到‘9‘表示從節點i到節點j需要耗費的時間。

輸出

包含一個整數,可能的路徑數,這個數可能很大,只需輸出這個數除以2009的余數。

樣例輸入

5 30
12045
07105
47805
12024
12345

樣例輸出

852


題解

矩陣乘法

傻題,顯然如果沒有邊權的話就是裸的矩乘。如果有邊權的話,拆邊的話點數為 $9m$ 的,難以接受。

考慮拆點,將1個點拆成9個連成鏈,每次將出點對應邊權的點連到入點上。這樣點數就是 $9n$ 了。

時間復雜度 $O((9n)^3)$

#include <cstdio>
#include <cstring>
char str[15];
int m;
struct data
{
	int v[100][100];
	data() {memset(v , 0 , sizeof(v));}
	int *operator[](int a) {return v[a];}
	data operator*(data &a)
	{
		data ans;
		int i , j , k;
		for(i = 0 ; i < m ; i ++ )
			for(j = 0 ; j < m ; j ++ )
				for(k = 0 ; k < m ; k ++ )
					ans[i][j] = (ans[i][j] + v[i][k] * a[k][j]) % 2009;
		return ans;
	}
}A;
data pow(data x , int y)
{
	data ans;
	int i;
	for(i = 0 ; i < m ; i ++ ) ans[i][i] = 1;
	while(y)
	{
		if(y & 1) ans = ans * x;
		x = x * x , y >>= 1;
	}
	return ans;
}
int main()
{
	int n , k , i , j;
	scanf("%d%d" , &n , &k) , m = n * 9;
	for(i = 0 ; i < n ; i ++ )
	{
		scanf("%s" , str);
		for(j = 0 ; j < 8 ; j ++ ) A[i * 9 + j][i * 9 + j + 1] = 1;
		for(j = 0 ; j < n ; j ++ )
			if(str[j] != ‘0‘)
				A[i * 9 + str[j] - ‘1‘][j * 9] = 1;
	}
	printf("%d\n" , pow(A , k)[0][n * 9 - 9]);
	return 0;
}

【bzoj1297】[SCOI2009]迷路 矩陣乘法