【个人赛组】2021-2022年度第三届全国大学生算法设计与编程挑战赛(秋季赛) G 巧克力

题目描述

简要题意:现在有一块长为 $L$ 的巧克力,我们可以将其切成若干段,不妨假设切成了 $m$ 段,每段的长度为 $a_i$,那么这种切法的价值就是 $\sum_{i=1}^ma_i^2$,现在给定 $n$ 个位置,这些位置不能切,求所有切法的价值和,答案对 $1e9+7$ 取模

$L\le 10^9,n\le 10^5$

Solution

我们考虑 $dp$,令 $f_i$ 表示切的位置在 $i$ 的价值和,容易得到 $f_i=\sum_{j=0}^{i-1}f_j(i-j)^2$

这个东西咋一看没法优化,但其实这个东西可以写成矩阵的形式,我们考虑用矩阵维护 $\sum_{i=0}^nf_i,\sum_{i=0}^n(n+1-i)f_i,\sum_{i=0}^n{(n+1-i)^2f_i}$,然后发现三个东西可以转移的,转移矩阵为

注意这个转移矩阵是左乘的,然后没法切的转移就是将第三列都减掉一个 $1$,时间复杂度 $O(n\log n)$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#define maxn 100010
#define ll long long
using namespace std;

const int p = 1000000007;
inline int add(int x, int y) { return (x += y) >= p ? x - p : x; }

struct Matrix {
static const int n = 4;
int a[n + 1][n + 1];

inline void clear() { fill(a[0], a[0] + (n + 1) * (n + 1), 0); }

Matrix() { clear(); }

inline void setone() { for (int i = 1; i <= n; ++i) a[i][i] = 1; }

friend Matrix operator * (const Matrix &u, const Matrix &v) {
Matrix w;
for (int k = 1; k <= n; ++k)
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
w.a[i][j] = add(w.a[i][j], 1ll * u.a[i][k] * v.a[k][j] % p);
return w;
}
} s[maxn];

Matrix pow(Matrix x, int n) {
Matrix s; s.setone();
for (; n; n >>= 1, x = x * x)
if (n & 1) s = s * x;
return s;
}

int n, L, a[maxn];

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> L >> n;
for (int i = 1; i <= n; ++i) cin >> a[i];
Matrix s, x, y; s.a[1][1] = s.a[2][1] = s.a[3][1] = 1;
x.a[1][1] = x.a[1][3] = x.a[2][1] = x.a[2][2] = x.a[2][3] = x.a[3][1] = 1; x.a[3][2] = x.a[3][3] = 2;
y.a[1][1] = y.a[2][1] = y.a[2][2] = y.a[3][1] = y.a[3][3] = 1; y.a[3][2] = 2;
for (int i = 1; i <= n; ++i) {
s = pow(x, a[i] - a[i - 1] - 1) * s;
if (i <= n) s = y * s;
}
cout << (pow(x, L - 1 - a[n]) * s).a[3][1] << "\n";
return 0;
}