UVa 11651 Krypton Number System DP + 矩陣快速冪

題意:

有一個\(base(2 \leq base \leq 6)\)進制系統,這裏面的數都是整數,不含前導0,相鄰兩個數字不相同。
並且每一個數字有一個得分\(score(1 \leq score \leq 10^9)\),得分爲 相鄰兩個數字之差的平方之和。ide

給出\(base\)\(score\),求知足條件的整數的個數 \(mod \, 2^{32}\)優化

分析:

首先考慮DP的作法:

\(dp(i, j)\)表示知足當前分數爲\(i\)最後一個數字是\(j\)的數字的個數。
遞推就是枚舉下一個數字\(k\),就有轉移方程:
\(dp(i+d,k)=\sum dp(i, j)\),其中\(k \neq j\)\(d=(k-j)^2\)
這種方法的複雜度使\(O(base^2 \cdot score)\)的。spa

考慮矩陣優化:

由於狀態轉移中,能獲得的最大分數是\((base-1)^2\),因此咱們的轉移矩陣只要保留前面\((base-1)^2\)\(score\)的信息就好了。
用語言不方便表達,我舉具體例子,
\(base=3\)時,有以下轉移:

上面9行很好理解,就是一個錯位。
下面3行纔是狀態的轉移。
容易看出,咱們的矩陣的邊長最大會達到\(6(6-1)^2=150\)code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef unsigned int LL;
const int maxn = 150;

int n, m, sz;

struct Matrix
{
	LL a[maxn][maxn];
	
	Matrix() {
		for(int i = 0; i < maxn; i++)
			for(int j = 0; j < maxn; j++) a[i][j] = 0;
	}
	
	Matrix operator * (const Matrix& t) const {
		Matrix ans;
		for(int i = 0; i < sz; i++)
			for(int j = 0; j < sz; j++) if(a[i][j])
				for(int k = 0; k < sz; k++)
					ans.a[i][k] += a[i][j] * t.a[j][k];
		return ans;
	}
};

Matrix pow_mod(Matrix a, int n) {
	Matrix ans;
	for(int i = 0; i < sz; i++) ans.a[i][i] = 1;
	while(n) {
		if(n & 1) ans = ans * a;
		a = a * a;
		n >>= 1;
	}
	return ans;
}

LL a[maxn], dp[25][6];

int main()
{
	int T; scanf("%d", &T);
	for(int kase = 1; kase <= T; kase++) {
		printf("Case %d: ", kase);
		scanf("%d%d", &n, &m);
		int N = (n - 1) * (n - 1) * n;

		//DP
		memset(dp, 0, sizeof(dp));
		for(int i = 1; i < n; i++) dp[0][i] = 1;
		for(int i = 0; i < (n - 1) * (n - 1); i++) {
			for(int j = 0; j < n; j++) {
				for(int k = 0; k < n; k++) {
					int d = (k - j) * (k - j);
					if(!d || i + d > (n - 1) * (n - 1)) continue;
					dp[i + d][k] += dp[i][j];
				}
			}
		}

		if(m < (n - 1) * (n - 1)) {
			LL ans = 0;
			for(int i = 0; i < n; i++) ans += dp[m][i];
			printf("%u\n", ans);
			continue;
		}

		sz = N;
		int s = (n - 1) * (n - 1);
		for(int i = 0; i < (n - 1) * (n - 1); i++)
			for(int j = 0; j < n; j++)
				a[i * n + j] = dp[i][j];
		Matrix M;
		
		for(int i = n; i < N; i++) M.a[i - n][i] = 1;
		for(int i = 0; i < n; i++)
			for(int j = 0; j < n; j++) if(j != i) {
				int d = (j - i) * (j - i);
				M.a[N - n + i][n * (s - d) + j] = 1;
			}
		M = pow_mod(M, m - (n - 1) * (n - 1) + 1);
		LL ans = 0;
		for(int i = N - n; i < N; i++)
			for(int j = 0; j < N; j++)
				ans += M.a[i][j] * a[j];
		printf("%u\n", ans);
	}

	return 0;
}
相關文章
相關標籤/搜索