题解 P5343 【【XR-1】分块】

· · 题解

Description

给定一个长度为 n\ (1 \leq n \leq 10^{18}) 的序列和 2可重 集合:PRNF 。现在要把它分成若干块,块的大小有限制,允许的块长为 PR \cap NF。求有多少种不同的分块方案,答案对 10^9 + 7 取模。(设最大块长为 x1 \leq |PR|,|NF|,x \leq 100)。

Solution

考虑 **动态规划** 。 先预处理出所有可能的块长 $a_1 \sim a_m$(注意要去重)。 用 $f_i$ 表示长度为 $i$ 的序列的分块方案数。 初始 $f_0 = 1$ 。(长度为 $0$ 的序列一定有 $1$ 种分块方案) 状态转移方程(考虑分出来最后一块的块长为 $a_j$): ![EwtYkD.png](https://s2.ax1x.com/2019/05/05/EwtYkD.png) 直接转移是 $O(n)$ 的。 $100\rm pts$: 发现块长最大只有 $100$($\max\limits_{i = 1}^m \{a_i\} \leq 100$),所以考虑用 **矩阵乘法** 优化上述 **DP** 。 若矩阵的大小为 $size \times size$,则有 $size = \max\limits_{i = 1}^m \{a_i\}$(因为 $f_i$ 不可能由 $f_{i - 100}$ 之前的转移过来)。 先构造初始矩阵。根据 $f_0 = 1$,**DP** 预处理出 $f_1 \sim f_{size - 1}$,然后才能转移。 ![EwtNfH.png](https://s2.ax1x.com/2019/05/05/EwtNfH.png) 接下来构造转移矩阵。首先根据 $f_i = \sum\limits_{j = 1}^m f_{i - a_j}$ 得知,矩阵第一行中,第 $a_i$ 个数为 $1$,其余为 $0$ 。剩下的 $f_{i-1} \sim f_{i - size + 1}$ 都可以在上一个矩阵中找到,所以对应的标为 $1$,其它为 $0$ 。 ![Ewtapd.png](https://s2.ax1x.com/2019/05/05/Ewtapd.png) 最后是答案矩阵。 ![Ewttte.png](https://s2.ax1x.com/2019/05/05/Ewttte.png) 答案矩阵的第 $1$ 行第 $1$ 列即是最终答案。时间复杂度为 $O(size^3\log n)$ 。 ## Code ```cpp #include <bits/stdc++.h> using namespace std; typedef long long LL; template <class T> inline void read(T &x) { x = 0; char c = getchar(); bool f = 0; for (; !isdigit(c); c = getchar()) f ^= c == '-'; for (; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); x = f ? -x : x; } template <class T> inline void write(T x) { if (x < 0) { putchar('-'); x = -x; } T y = 1; int len = 1; for (; y <= x / 10; y *= 10) ++len; for (; len; --len, x %= y, y /= 10) putchar(x / y + 48); } const int MAXN = 100; const int MOD = 1e9 + 7; LL n; int pr, nf, m, size, f[MAXN + 5]; bool a[MAXN + 5], b[MAXN + 5]; struct Matrix { int mat[MAXN + 5][MAXN + 5]; inline void clear() { memset(mat, 0, sizeof (mat)); } inline Matrix friend operator*(Matrix a, Matrix b) { Matrix c; c.clear(); for (int i = 1; i <= size; ++i) for (int j = 1; j <= size; ++j) for (int k = 1; k <= size; ++k) c.mat[i][j] = (c.mat[i][j] + (LL) a.mat[i][k] * b.mat[k][j]) % MOD; return c; }//矩阵乘法 } ans, base; inline Matrix quickPow(Matrix x, LL p) { Matrix res; res.clear(); for (int i = 1; i <= size; ++i) res.mat[i][i] = 1; for (; p; p >>= 1, x = x * x) if (p & 1) res = res * x; return res; }//矩阵快速幂 int main() { read(n), read(pr); for (int x, i = 1; i <= pr; ++i) { read(x); a[x] = 1; } read(nf); for (int x, i = 1; i <= nf; ++i) { read(x); b[x] = 1; }//用两个桶标记允许的块长 for (int i = 1; i <= 100; ++i) if (a[i] && b[i]) {//若同时满足 a,b base.mat[1][i] = 1;//转移矩阵第一行 size = i;//矩阵大小 } for (int i = 1; i < size; ++i) base.mat[i + 1][i] = 1;//转移矩阵第 2 ~ size 行 f[0] = 1;//初始状态 for (int i = 1; i < size; ++i) for (int j = 1; j <= i; ++j) if (a[j] && b[j]) f[i] = (f[i] + f[i - j]) % MOD;//DP 预处理 f[1] ~ f[size - 1] for (int i = 1; i <= size; ++i) ans.mat[i][1] = f[size - i];//初始矩阵 ans = quickPow(base, n - size + 1) * ans;//得到答案矩阵 write(ans.mat[1][1]); putchar('\n'); return 0; } ```