P2768 珍珠项链 题解
Link。
题目大意
有
思路
这题很明显是动态规划,我们假设
则
最终答案就是:
所以直接动态规划即可,注意多组数据。
#include <cstdio>
#define int long long
const int N = 5e4 + 5, K = 35, Mod = 1234567891;
int n, k, T, f[N][K], ans;
signed main() {
scanf("%lld", &T);
while (T--) {
scanf("%lld %lld", &n, &k);
f[0][0] = 1, ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= k; j++)
f[i][j] = (f[i - 1][j] * j % Mod + f[i - 1][j - 1] * (k - j + 1) % Mod) % Mod;
ans = (ans + f[i][k]) % Mod;
}
printf("%lld\n", ans);
}
return 0;
}
可是我们把这样一份代码交上去后,发现:Link。
这时,我发现
数组都开不了这么大。
这个时候就要请出我们的主角——矩阵加速(不会的戳这里)。
关于这道题,我们先看到动态规划的部分:
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= k; j++)
f[i][j] = (f[i - 1][j] * j % Mod + f[i - 1][j - 1] * (k - j + 1) % Mod) % Mod;
ans = (ans + f[i][k]) % Mod;
}
由于每个长度下我们都需要更新完每种情况我们才能继续更新更新下一个长度。
所以初始矩阵一定包含
而我们需要统计:
以
初始矩阵为:
现在我们需要寻找到一个矩阵和初始矩阵相乘得到:
根据递推式,我们可以知道加速矩阵应该如下:
所以用初始矩阵乘加速矩阵的
最终答案为结果矩阵的最后一项。
code
#include <cstdio>
#include <cstring>
#define int long long
const int N = 1e6 + 5, K = 35, Mod = 1234567891;
int n, k, T;
struct Matrix {
int n, m, a[K][K];
Matrix() { memset(a, 0, sizeof(a)); }
Matrix operator * (const Matrix &c) { // 矩阵乘法
Matrix res;
res.n = n, res.m = c.m;
for (int i = 1; i <= 31; i++)
for (int j = 1; j <= 31; j++)
for (int k = 1; k <= 31; k++)
res.a[i][j] = (res.a[i][j] + a[i][k] * c.a[k][j] % Mod) % Mod;
return res;
}
} s, ans;
inline void qkpow(int x) { // 矩阵快速幂
while (x) {
if (x & 1)
ans = ans * s;
s = s * s;
x >>= 1;
}
}
signed main() {
scanf("%lld", &T);
while (T--) {
scanf("%lld %lld", &n, &k);
memset(ans.a, 0, sizeof(ans.a));
memset(s.a, 0, sizeof(s.a)); // 每次清 0,不然会错
ans.n = 1, ans.m = k + 1, ans.a[1][1] = k; // f[1][1] 为 k
s.n = k + 1, s.m = k + 1;
for (int i = 1; i <= k; i++)
s.a[i][i] = i, s.a[i - 1][i] = k - i + 1;
s.a[k + 1][k + 1] = s.a[k][k + 1] = 1; // 根据刚才推出的规律得到初始矩阵
qkpow(n);
printf("%lld\n", ans.a[1][k + 1]);
}
return 0;
}