UVa 11651 Krypton Number System DP + 矩阵快速幂

题意:

有一个\(base(2 \leq base \leq 6)\)进制系统,这里面的数都是整数,不含前导0,相邻两个数字不相同。
而且每个数字有一个得分\(score(1 \leq score \leq 10^9)\),得分为 相邻两个数字之差的平方之和。

给出\(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)\)的。

考虑矩阵优化:

因为状态转移中,能得到的最大分数是\((base-1)^2\),所以我们的转移矩阵只要保留前面\((base-1)^2\)\(score\)的信息就行了。
用语言不方便表达,我举具体例子,
\(base=3\)时,有如下转移:

上面9行很好理解,就是一个错位。
下面3行才是状态的转移。
容易看出,我们的矩阵的边长最大会达到\(6(6-1)^2=150\)

#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;
}
posted @ 2016-03-09 00:42  AOQNRMGYXLMV  阅读(1080)  评论(2编辑  收藏  举报