由于CSDN编辑的不方便,所以给出一篇别人的讲解:http://www.cnblogs.com/AOQNRMGYXLMV/p/5256508.html
但是他的转移出现了错误。正确的矩阵相乘应该是这样
题目意思是给定一个进制base和一个score,问在该进制下有多少数满足下列条件:相邻两个数字不相同,前缀不能为0,所有相邻数字的差的平方和位score,所有数字都是整数。
这个题目难度主要集中在找递推式子,一开始确实没有思路,看了一个人的博客,知道用DP和矩阵快速幂做,试着去推DP的方程
有dp[i][j]表示当score = i时数字末尾为j的数字的个数。
我们拿4进制简单推一下,首先初始化显然有,然后继续推score = 1的情况:,继续推score = 2的情况:,继续推score = 3的情况:,继续推score =4的情况:
,我说,这样一直递推到8就可以了,为什么呢?因为如果我们把0作为第一个节点,那么0下一次最大只能到达,也就是9,这样0-8就形成了一个循环节。显然0 -> 9, 1 -> 10 ... 8 -> 17都是一样的递推式子。这样我们就得到了递推的关系式,然后就可以用矩阵快速幂优化计算,最后就能得到答案。
到只需要将第一个矩阵的后4 * 8项依次往前平移4项,然后第二个矩阵最后四项的则需要利用上面的递推关系式得到。我们只需要得到后,利用矩阵快速幂就可以求得任意一项的值
我的心得体会:在初始化 这个dp数组时候有两种初始化方法,也意味着两种不同的理解。第一种是上面的这样,还有一种就是dp[0][..]全部初始化为1,然后计算答案的时候就需要从当score为n时最后一位为1到base-1开始相加。可以这样理解,这样初始化意味着允许出现前导零,但数的最后一位不能是零,这和题目要求其实是一样的作用。
还有就是循环节,因为每次增加一个数score最大只能增加,所以每轮计算(n-1)*(n-1)个score的模式是一样的。代码对于下标的各种用法,还是很有技巧性的。最后还有一点在代码的注释中。。
第一种初始化dp的代码:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef unsigned int LL;
const int maxn = 155;
int n, m, sz;
struct Matrix
{
LL mat[maxn][maxn];
Matrix ()
{
memset(mat, 0, sizeof(mat));
}
Matrix operator * (const Matrix& other) const
{
Matrix tem;
for(int i = 0; i < sz; i++)
for(int j = 0; j < sz; j++)
if(mat[i][j])
for(int k = 0; k < sz; k++)
tem.mat[i][k] += mat[i][j] * other.mat[j][k];
return tem;
}
};
Matrix fast_mod(Matrix base, int power)
{
Matrix ans;
for(int i = 0; i < sz; i++)
ans.mat[i][i] = 1;
while(power)
{
if(power&1)
ans = ans * base;
base = base * base;
power >>= 1;
}
return ans;
}
LL a[maxn], dp[30][10];
int main()
{
int T;
scanf("%d", &T);
for(int kase = 1; kase <= T; kase++)
{
scanf("%d%d", &n, &m);
printf("Case %d: ", kase);
int N = (n-1) * (n-1) * n;
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 base;
for(int i = n; i < N; i++)
base.mat[i-n][i] = 1;
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
{
int d = (j - i) * (j - i);
base.mat[N - n + i][n * (s - d) + j] = 1; //之所以是n * (s - d) + j,因为s-d意味着是从哪个数转移过来的,所以乘n就很好理解了,+j纯属下标的理解
}
base = fast_mod(base, 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 += base.mat[i][j] * a[j];
printf("%u\n", ans);
}
return 0;
}
第二种初始化代码:(结构体内各种重载!!)
#include <stdio.h>
#include <string.h>
const unsigned long long mod = 1LLU<<32;
struct Matrix {
unsigned long long v[150][150];
int row, col; // row x col
Matrix(int n, int m, long long a = 0) {
memset(v, 0, sizeof(v));
row = n, col = m;
for(int i = 0; i < row && i < col; i++)
v[i][i] = a;
}
Matrix operator*(const Matrix& x) const {
Matrix ret(row, x.col);
for(int i = 0; i < row; i++) {
for(int k = 0; k < col; k++) {
if (v[i][k])
for(int j = 0; j < x.col; j++) {
ret.v[i][j] += v[i][k] * x.v[k][j],
ret.v[i][j] %= mod;
}
}
}
return ret;
}
Matrix operator^(const int& n) const {
Matrix ret(row, col, 1), x = *this;
int y = n;
while(y) {
if(y&1) ret = ret * x;
y = y>>1, x = x * x;
}
return ret;
}
};
int main() {
int testcase, cases = 0, base, score;
scanf("%d", &testcase);
while(testcase--) {
scanf("%d %d", &base, &score);
printf("Case %d: ", ++cases);
int N = (base - 1) * (base - 1);
unsigned long long dp[64][64] = {};
for (int i = 0; i <= N; i++)
dp[0][i] = 1;
for (int i = 0; i < N; i++) {
for (int j = 0; j < base; j++) {
for (int k = 0; k < base; k++) {
int d = (j - k) * (j - k);
if (i + d > N || j == k)
continue;
dp[i+d][k] += dp[i][j];
dp[i+d][k] %= mod;
}
}
}
if (score <= N) {
unsigned long long ret = 0;
for (int i = 1; i < base; i++)
ret = (ret + dp[score][i])%mod;
printf("%llu\n", ret);
continue;
}
int r, c;
r = c = (base - 1) * (base - 1) * base;
Matrix x(r, c), y(c, 1);
for (int i = 1; i <= N; i++)
for (int j = 0; j < base; j++)
y.v[(i-1) * base+j][0] = dp[i][j];
for (int i = base; i < r; i++)
x.v[i - base][i] = 1;
for (int i = 0; i < base; i++) {
for (int j = 0; j < base; j++) {
if (i == j) continue;
int d = N - (i - j) * (i - j);
x.v[(N-1)*base + i][d*base + j] = 1;
}
}
Matrix z = (x ^ (score - N)) * y;
unsigned long long ret = 0;
for (int i = 1; i < base; i++)
ret = (ret + z.v[(N-1) * base + i][0])%mod;
printf("%llu\n", ret);
}
return 0;
}