题意:给定一个进制base和一个score,问在该进制下有多少数满足下列条件:相邻两个数字不相同,前缀不能为0,所有相邻数字的差的平方和为score,所有数字都是整数。结果取模2^32(unsigned int)。
题解:dp+矩阵快速幂
令
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示满足当前分数为
i
i
i最后一个数字是
j
j
j的数字的个数。
可以得到状态转移方程:
d
p
[
i
+
d
i
s
]
[
j
]
=
∑
d
p
[
i
]
[
k
]
dp[i+dis][j]=∑dp[i][k]
dp[i+dis][j]=∑dp[i][k],其中
d
i
s
=
(
k
−
j
)
2
dis=(k−j)^2
dis=(k−j)2。
时间复杂度为
O
(
s
c
o
r
e
∗
b
a
s
e
2
)
O(score*base^2)
O(score∗base2),1e9,肯定超时。
考虑用矩阵快速幂来优化。
我们发现,在状态转移中,分数最多变化
(
b
a
s
e
−
1
)
2
(base-1)^2
(base−1)2,我们先预处理分数在
(
b
a
s
e
−
1
)
2
(base-1)^2
(base−1)2内的
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]。
举个例子,base = 3。图片来源,转移过程讲的很详细。
我们发现1-9行相当于将之前状态左移了一下,最后3行就是状态的转移,具体可以见代码注释。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#include<sstream>
#include<iomanip>
#define ll unsigned int
using namespace std;
//矩阵快速幂
int sz;
struct matrix {
ll mat[155][155];
matrix() { memset(mat, 0, sizeof(mat)); }
matrix operator * (const matrix& b)const {
matrix ans;
for (int i = 0; i < sz; i++) {
for (int j = 0; j < sz; j++) {
ans.mat[i][j] = 0;
for (int k = 0; k < sz; k++) {
ans.mat[i][j] = ans.mat[i][j] + mat[i][k] * b.mat[k][j];
}
}
}
return ans;
}
};
matrix q_pow(matrix a, ll b) {
matrix ans;
memset(ans.mat, 0, sizeof(ans.mat));
for (int i = 0; i < sz; i++) ans.mat[i][i] = 1;
while (b) {
if (b & 1) ans = ans * a;
b >>= 1;
a = a * a;
}
return ans;
}
int t, ba, sc;
ll dp[25][11], a[155];
int main() {
scanf("%d", &t);
int cas = 0;
while (t--) {
memset(dp, 0, sizeof(dp));
scanf("%d%d", &ba, &sc);
for (int i = 1; i < ba; i++) dp[0][i] = 1;
for (int i = 0; i < (ba - 1) * (ba - 1); i++) {
for (int j = 0; j < ba; j++) {
for (int k = 0; k < ba; k++) {
int dis = (j - k) * (j - k);
if (!dis || i + dis >= (ba - 1) * (ba - 1)) continue;
dp[i + dis][j] += dp[i][k];
}
}
}
if (sc < (ba - 1) * (ba - 1)) {
ll ans = 0;
for (int i = 0; i < ba; i++) ans += dp[sc][i];
printf("Case %d: %u\n", ++cas, ans);
continue;
}
for (int i = 0; i < (ba - 1) * (ba - 1); i++) {
for (int j = 0; j < ba; j++) {
a[i * ba + j] = dp[i][j]; //初始矩阵
}
}
matrix ma;
sz = (ba - 1) * (ba - 1) * ba;
for (int i = ba; i < sz; i++) {
ma.mat[i - ba][i] = 1; //左移
}
for (int i = 0; i < ba; i++) { //dp[(ba-1)*(ba-1)][i],新分数
for (int j = 0; j < ba; j++) { //dp[(ba-1)*(ba-1)-dis][j],找转移状态
if (i == j) continue;
int dis = (i - j) * (i - j);
ma.mat[sz - ba + i][((ba - 1) * (ba - 1) - dis) * ba + j] = 1;
}
}
ma = q_pow(ma, sc - (ba - 1) * (ba - 1) + 1);
ll ans = 0;
for (int i = 0; i < ba; i++) {
for (int j = 0; j < sz; j++) {
ans += ma.mat[sz - ba + i][j] * a[j];
}
}
printf("Case %d: %u\n", ++cas, ans);
}
return 0;
}