题解 P5343 【【XR-1】分块】
题是简单题,重在想到矩乘后怎么接着往下写。
题意不须讲了,很清楚而且很有意思。
转移方程稍微给一下,推导可以阅读别的题解。
设置状态
我们有: (
我们看到块总长是
因为开不下,所以
『递推的优化,显然矩乘。』
我们尽量少算值,整出个初始状态就差不多可以了。
要推第
因为块长不会大于
开
现在就到了矩阵乘法最重要的一部分————初始化矩阵。
目前这一步的状态需要前面哪几步的状态推出?
我们通过递推式知道了,与
我们把
也就是说新的矩阵的
那转移矩阵的第一行里必然有:对于每一个可行的
比如对于样例1,
我们解决了第一行,那其他行如何处理?
我们此刻的
这一过程在做其他矩乘题里必然也经常用到,对于第
这样可以做到原来在
对于后面几位亦如此。
最终成品转移矩阵如下(中间省略了一部分数值):
所以 AC 此题,流程如下:
-
预处理
dp_{\ 100} ,填入原始矩阵。 -
初始化转移矩阵。
-
矩阵快速幂。
-
输出最终答案矩阵的
(1,1) 。
以下是代码~
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MOD = 1e9 + 7;
int LEN, PR, NF;
map<int, int> m1, m2;
int N;
int dp[107];
/*矩阵组件*/
struct matrix
{
int num[107][107];
matrix operator*(const matrix a) const
{
matrix c;
memset(c.num, 0, sizeof(c.num));
for (int i = 1; i <= N; i++)
{
for (int j = 1; j <= N; j++)
{
for (int k = 1; k <= N; k++)
{
c.num[i][j] = (c.num[i][j] + (num[i][k] * a.num[k][j]) % MOD) % MOD;
}
}
}
return c;
}
} ORZ, RBQ;//ORZ----原始矩阵 RBQ----转移矩阵
/*快速幂组件*/
matrix quick_pow(matrix a, int p)
{
if (p == 1)
{
return a;
}
matrix tmp = quick_pow(a, p / 2);
if (p % 2 == 0)
{
return tmp * tmp;
}
else
{
return tmp * tmp * a;
}
}
signed main()
{
memset(ORZ.num, 0, sizeof(ORZ.num));
memset(RBQ.num, 0, sizeof(RBQ.num));
cin >> LEN;
cin >> PR;
for (int i = 1; i <= PR; i++)
{
int num;
cin >> num;
m1[num] = 1;
}
cin >> NF;
for (int i = 1; i <= NF; i++)
{
int num;
cin >> num;
if (m1[num])
{
m2[num] = 1;//指这个块长两人是否都公认
}
}
/*初始化转移矩阵*/
for (int i = 1; i <= 100; i++)
{
if (m2[i] == 1)
{
RBQ.num[1][i] = 1;
N = i;
}
}
for (int i = 1; i < N; i++)
{
RBQ.num[i + 1][i] = 1;
}
/*预处理dp[100]*/
dp[0] = 1;
for (int i = 1; i < N; i++)
{
for (int j = 1; j <= i; j++)
{
if (m2[j] == 1)
{
dp[i] = (dp[i] + dp[i - j]) % MOD;
}
}
ORZ.num[N - i][1] = dp[i];
}
ORZ.num[N][1] = 1;
matrix ans = quick_pow(RBQ, LEN - N + 1) * ORZ;
cout << ans.num[1][1];
}