【XR-1】分块

题目link:https://www.luogu.com.cn/problem/P5343


Part1:

首先这道题能够想到一个比较显然的 $dp$ 。

设 $dp[i]$ 表示长度为 $i$ 的序列有几种分块方式。

那么容易的出转移方程: $dp[i]$ $=$ $∑$ $dp[i$ $-$ $p[j]]$ $(1$ $≤$ $j$ $≤$ $l)$ ,其中 $l$ 为相同长度的块数, $p[j]$ 表示第 $j$ 个相同长度的块。

这样便可以 $O(n)$ 求出答案,可以通过 $60$$\%$ 的数据,但是 $100$$\%$ 的数据中 $0$ $≤$ $n$ $≤$ $10^{18}$ 。


Part2:

考虑优化,显然这个递推式是一个常系数线性递推,而这个 $l$ 的大小最大为 $100$ ,因此可以用矩阵快速幂优化。

因此只需要预处理出前 $100$ 的 $dp$ 值,之后构造初始向量和转移矩阵就可以了。


时间复杂度 $O($$x^3$ $*$ $log$ $k)$。


Code:

  1 #include <cstdio>
  2 #include <algorithm>
  3 
  4 typedef long long LL;
  5 
  6 const int MAXN = 100;
  7 const LL MOD = 1e9 + 7;
  8 
  9 LL n, dp[MAXN + 5];
 10 int pr, nf, prPiece[MAXN + 5], nfPiece[MAXN + 5];
 11 int len, piece[MAXN + 5], x;
 12 
 13 struct Mat {
 14 
 15     int N, M;
 16     LL arr[MAXN + 5][MAXN + 5];
 17 
 18     Mat() {}
 19     Mat(int _N, int _M, LL val = 0ll) {
 20         N = _N, M = _M;
 21         for(int i = 1; i <= N; ++i) {
 22             for(int j = 1; j <= M; ++j) {
 23                 arr[i][j] = (i == j) ? val : 0ll;
 24             }
 25         }
 26     }
 27 
 28     // void print() {
 29     //     for(int i = 1; i <= N; ++i) {
 30     //         for(int j = 1; j <= M; ++j) {
 31     //             printf("%lld%c", arr[i][j], j == M ? '\n' : ' ');
 32     //         }
 33     //     }
 34     //     puts("");
 35     // }
 36 
 37 };
 38 
 39 Mat operator* (const Mat &A, const Mat &B) {
 40 
 41     Mat res(A.N, B.M);
 42 
 43     for(int i = 1; i <= res.N; ++i) {
 44         for(int j = 1; j <= res.M; ++j) {
 45             for(int k = 1; k <= A.M; ++k) {
 46                 res.arr[i][j] += (A.arr[i][k] * B.arr[k][j]) % MOD;
 47                 res.arr[i][j] %= MOD;
 48             }
 49         }
 50     }
 51 
 52     return res;
 53 }
 54 
 55 Mat qpow(Mat A, LL k) {
 56 
 57     Mat res(A.N, A.M, 1);
 58 
 59     for(; k; k >>= 1) {
 60         if(k & 1) res = res * A;
 61         A = A * A;
 62     }
 63 
 64     return res;
 65 }
 66 
 67 int max(int a, int b) {
 68     return a > b ? a : b;
 69 }
 70 
 71 int main() {
 72 
 73     scanf("%lld", &n);
 74 
 75     scanf("%d", &pr);
 76     for(int i = 1; i <= pr; ++i) {
 77         scanf("%d", &prPiece[i]);
 78     }
 79 
 80     scanf("%d", &nf);
 81     for(int i = 1; i <= nf; ++i) {
 82         scanf("%d", &nfPiece[i]);
 83     }
 84 
 85     std::sort(prPiece + 1, prPiece + pr + 1);
 86     std::sort(nfPiece + 1, nfPiece + nf + 1);
 87     int ptPr = 1, ptNf = 1;
 88     while(ptPr <= pr && ptNf <= nf) {
 89         if(prPiece[ptPr] < nfPiece[ptNf]) {
 90             ++ptPr;
 91         }
 92         else if(prPiece[ptPr] > nfPiece[ptNf]) {
 93             ++ptNf;
 94         }
 95         else {
 96             if(prPiece[ptPr] != prPiece[ptPr - 1] && nfPiece[ptNf] != nfPiece[ptNf - 1]) {
 97                 piece[++len] = prPiece[ptPr];
 98             }
 99             ++ptPr, ++ptNf;
100         }
101     }
102 
103     for(int i = 1; i <= len; ++i) {
104         x = max(x, piece[i]);
105     }
106 
107     Mat fac(x, x), vec(1, x);
108     dp[0] = 1ll;
109     for(int i = 1; i <= x; ++i) {
110         for(int j = 1; j <= len; ++j) {
111             if(i - piece[j] < 0) break;
112             dp[i] += dp[i - piece[j]];
113             dp[i] %= MOD;
114         }
115     }
116 
117     for(int i = 1; i <= x; ++i) {
118         vec.arr[1][i] = dp[x - i + 1];
119     }
120 
121     for(int i = 1; i <= len; ++i) {
122         fac.arr[piece[i]][1] = 1ll;
123     }
124 
125     for(int i = 1; i <= fac.N; ++i) {
126         for(int j = 2; j <= fac.M; ++j) {
127             fac.arr[i][j] = (i == j - 1) ? 1ll : 0ll;
128         }
129     }
130 
131     if(n <= x) {
132         printf("%lld\n", dp[n]);
133     }
134     else {
135         printf("%lld\n", (vec * qpow(fac, n - x)).arr[1][1]);
136     }
137 
138     return 0;
139 }

 

posted @ 2021-07-26 10:56  louis_11  阅读(74)  评论(0编辑  收藏  举报