Discription
D
a
r
r
e
l
l
Darrell
Darrell 在思考一道计算题。
给你一个尺寸为
1
×
N
1 × N
1×N 的长条,你可以在上面切很多刀,要求竖直地切并且且完后每块的长度都是整数。
在这种限制下其实只有
N
−
1
N − 1
N−1 个位置可以切。
对于一种切的方案,假如切完后每块的宽度分别是:
w
1
,
w
2
,
w
3
,
.
.
.
,
w
k
(
∑
w
i
=
N
)
w_1, w_2, w_3, ..., w_k(\sum w_i = N)
w1,w2,w3,...,wk(∑wi=N),那么该种方案对应
的完美值为:
∏
i
=
1
k
w
i
2
∏^{k}_{i=1}w_i^2
i=1∏kwi2
那么问题来了,给出
M
M
M 个位置不能切(如果用
x
x
x 表示一个位置,那么切完该位置后长条变为
1
×
x
1 × x
1×x 和
1
×
(
N
−
x
)
1 × (N − x)
1×(N−x) 两块),那么所有合法切法对应的完美值的和是多少呢?(只需要输出模
1
0
9
+
7
10^9 + 7
109+7 的结果)
Input
第
1
1
1 行,有
2
2
2 个整数:
N
M
N M
NM,表示长条的总长度及不能切的位置数
第
2
2
2 行,有
M
M
M 个整数:
x
1
,
x
2
,
x
3
,
.
.
.
,
x
M
x_1, x_2, x_3, ..., x_M
x1,x2,x3,...,xM 表示不能切的位置。
Output
输出 1 1 1 行,包含 1 1 1 个整数,表示满足要求的所有切法对应的完美值和。(对 1 0 9 + 7 10^9 + 7 109+7 取模后的结果)
Note
• 对于
20
%
20\%
20% 的数据,有
1
≤
N
,
M
≤
1
0
3
1 ≤ N, M ≤ 10^3
1≤N,M≤103。
• 对于
60
%
60\%
60% 的数据,有
1
≤
N
,
M
≤
1
0
5
1 ≤ N, M ≤ 10^5
1≤N,M≤105。
• 对于
100
%
100\%
100% 的数据,有
1
≤
N
≤
1
0
9
,
1
≤
M
≤
1
0
5
1 ≤ N ≤ 10^9,1 ≤ M ≤ 10^5
1≤N≤109,1≤M≤105,且
1
≤
x
1
<
x
2
<
x
3
<
.
.
.
<
x
m
<
N
1 ≤ x_1 < x_2 < x_3 < ... < x_m < N
1≤x1<x2<x3<...<xm<N
很显然可以看出 D P DP DP柿子 d p [ 0 ] = 1 , d p [ i ] = ∑ j = 0 i − 1 d p [ j ] ∗ ( i − j ) 2 dp[0]=1,\ \ \ dp[i]=\sum_{j=0}^{i-1}dp[j]*(i-j)^2 dp[0]=1, dp[i]=j=0∑i−1dp[j]∗(i−j)2
然后答案就为 d p [ N ] dp[N] dp[N]
由于 N N N很大 ≤ 1 0 9 \le10^9 ≤109
所以考虑用矩阵快速幂优化转移
先想想 M = 0 M=0 M=0怎么转移
定义向量如下: [ ∑ j = 0 i d p [ j ] , ∑ j = 0 i d p [ j ] ∗ ( i − j ) , ∑ j = 0 i d p [ j ] ∗ ( i − j ) 2 ] \left[\ \ \ \sum_{j=0}^idp[j]\ ,\ \sum_{j=0}^idp[j]*(i-j)\ ,\ \sum_{j=0}^idp[j]*(i-j)^2\ \ \ \right] [ j=0∑idp[j] , j=0∑idp[j]∗(i−j) , j=0∑idp[j]∗(i−j)2 ]
然后考虑从 [ ∑ j = 0 i − 1 d p [ j ] , ∑ j = 0 i − 1 d p [ j ] ∗ ( i − 1 − j ) , ∑ j = 0 i − 1 d p [ j ] ∗ ( i − 1 − j ) 2 ] \left[\ \ \ \sum_{j=0}^{i-1}dp[j]\ ,\ \sum_{j=0}^{i-1}dp[j]*(i-1-j)\ ,\ \sum_{j=0}^{i-1}dp[j]*(i-1-j)^2\ \ \ \right] [ j=0∑i−1dp[j] , j=0∑i−1dp[j]∗(i−1−j) , j=0∑i−1dp[j]∗(i−1−j)2 ]转移到 [ ∑ j = 0 i d p [ j ] , ∑ j = 0 i d p [ j ] ∗ ( i − j ) , ∑ j = 0 i d p [ j ] ∗ ( i − j ) 2 ] \left[\ \ \ \sum_{j=0}^idp[j]\ ,\ \sum_{j=0}^idp[j]*(i-j)\ ,\ \sum_{j=0}^idp[j]*(i-j)^2\ \ \ \right] [ j=0∑idp[j] , j=0∑idp[j]∗(i−j) , j=0∑idp[j]∗(i−j)2 ]
转移矩阵就是
[
2
2
1
1
1
0
1
2
1
]
[
∑
j
=
0
i
−
1
d
p
[
j
]
∑
j
=
0
i
−
1
d
p
[
j
]
∗
(
i
−
1
−
j
)
∑
j
=
0
i
−
1
d
p
[
j
]
∗
(
i
−
1
−
j
)
2
]
=
[
∑
j
=
0
i
d
p
[
j
]
∑
j
=
0
i
d
p
[
j
]
∗
(
i
−
j
)
∑
j
=
0
i
d
p
[
j
]
∗
(
i
−
j
)
2
]
\left[\begin{matrix} 2&2&1\\ 1&1&0\\ 1&2&1\\ \end{matrix}\right] \left[\begin{matrix} \sum_{j=0}^{i-1}dp[j]\\ \sum_{j=0}^{i-1}dp[j]*(i-1-j)\\ \sum_{j=0}^{i-1}dp[j]*(i-1-j)^2\\ \end{matrix}\right] =\left[\begin{matrix} \sum_{j=0}^{i}dp[j]\\ \sum_{j=0}^{i}dp[j]*(i-j)\\ \sum_{j=0}^{i}dp[j]*(i-j)^2\\ \end{matrix}\right]
⎣⎡211212101⎦⎤⎣⎢⎡∑j=0i−1dp[j]∑j=0i−1dp[j]∗(i−1−j)∑j=0i−1dp[j]∗(i−1−j)2⎦⎥⎤=⎣⎢⎡∑j=0idp[j]∑j=0idp[j]∗(i−j)∑j=0idp[j]∗(i−j)2⎦⎥⎤自己推一推吧…
注意第二项和第三项当 j = i j=i j=i时贡献的值是 0 0 0因为 i − j = 0 i-j=0 i−j=0,所以 j j j的范围实质上是 [ 0 , i − 1 ] [0,i-1] [0,i−1].为了统一就写成 [ 0 , i ] [0,i] [0,i]了.
考虑这个地方不能切,我们就强制把这个位置的 d p dp dp值设为 0 0 0就行了.
那么不能切的地方的转移,就只用把第一项累加
d
p
[
i
]
dp[i]
dp[i]的系数去掉就行了.第一行也就变成了
[
2
,
2
,
1
]
−
[
1
,
2
,
1
]
=
[
1
,
0
,
0
]
[2,2,1]-[1,2,1]=[1,0,0]
[2,2,1]−[1,2,1]=[1,0,0],那么不能切的地方的转移矩阵就如下:
[
1
0
0
1
1
0
1
2
1
]
\left[\begin{matrix} 1&0&0\\ 1&1&0\\ 1&2&1\\ \end{matrix}\right]
⎣⎡111012001⎦⎤
详细见代码
CODE
#include<bits/stdc++.h>
using namespace std;
inline void read(int &num) {
char ch; int flg = 1; while(!isdigit(ch=getchar()))if(ch=='-')flg = -flg;
for(num=0; isdigit(ch); num=num*10+ch-'0', ch=getchar()); num*=flg;
}
const int MAXN = 1e5+5;
const int mod = 1e9+7;
struct mat {
int a[3][3];
mat() { memset(a,0,sizeof a); }
inline void init1() {
a[0][0] = 2; a[0][1] = 2; a[0][2] = 1;
a[1][0] = 1; a[1][1] = 1; a[1][2] = 0;
a[2][0] = 1; a[2][1] = 2; a[2][2] = 1;
}
inline void init2() {
a[0][0] = 1; a[0][1] = 0; a[0][2] = 0;
a[1][0] = 1; a[1][1] = 1; a[1][2] = 0;
a[2][0] = 1; a[2][1] = 2; a[2][2] = 1;
}
inline mat operator *(const mat &o)const {
mat re;
for(int k = 0; k < 3; ++k)
for(int i = 0; i < 3; ++i) if(a[i][k])
for(int j = 0; j < 3; ++j) if(o.a[k][j])
re.a[i][j] = (re.a[i][j] + 1ll * a[i][k] * o.a[k][j] % mod) % mod;
return re;
}
inline mat operator ^(int b)const {
mat A = *this, re;
re.a[0][0] = re.a[1][1] = re.a[2][2] = 1;
while(b) {
if(b & 1) re = re * A;
A = A * A; b >>= 1;
}
return re;
}
}ans, trans1, trans2;
int N, M, pos[MAXN];
int main() {
freopen("count.in", "r", stdin);
freopen("count.out", "w", stdout);
scanf("%d%d", &N, &M);
for(int i = 1; i <= M; ++i)
scanf("%d", &pos[i]);
ans.a[0][0] = 1;
trans1.init1();
trans2.init2();
for(int i = 1; i <= M; ++i)
ans = (trans1^(pos[i]-pos[i-1]-1)) * ans, ans = trans2 * ans;
ans = (trans1^(N-pos[M])) * ans;
printf("%d\n", ans.a[2][0]);
}
妈妈我终于会矩阵快速幂了!!!