Loj 2253 礼物

二项式定理 + 矩阵快速幂.

设 $f(i)$ 表示前 $i$ 个人带来的礼物总和,显然有 $f(0)=0,f(i)=2f(i-1)+i^k$ .

答案为 $f(n)-f(n-1)=f(n-1)+n^k$ ,于是只需要设法求出 $f(n-1)$ .

用矩阵快速幂加速递推,需要将 $i^k$ 也通过一些元素线性表示出来.

考虑将它用二项式定理展开,
$$
i^k=((i-1)+1)^k=\sum_{j=0}^k (i-1)^j \binom{k}{j}
$$
可以发现 $i^k$ 可以由 $(i-1)^0,(i-1)^1,\dots,(i-1)^k$ 线性表示.

于是向量中维护 $i^0,i^1,\dots,i^k,f(i)$ 这些元素,用矩阵快速幂加速转移.

时间复杂度 $O(k^3\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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//%std
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
inline ll read()
{
ll out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
fh=-1,jp=getchar();
while (jp>='0'&&jp<='9')
out=out*10+jp-'0',jp=getchar();
return out*fh;
}
const int P=1e9+7;
int add(int a,int b)
{
return (a+b>=P)?(a+b-P):(a+b);
}
void inc(int &a,int b)
{
a=add(a,b);
}
int mul(int a,int b)
{
return 1LL * a * b % P;
}
int fpow(int a,int b)
{
int res=1;
while(b)
{
if(b&1)
res=mul(res,a);
a=mul(a,a);
b>>=1;
}
return res;
}
const int K=12;
int k;
struct Matrix
{
int v[K][K];
Matrix(){memset(v,0,sizeof v);}
Matrix operator * (const Matrix &rhs) const
{
Matrix res;
for(int i=0;i<=k+1;++i)
for(int p=0;p<=k+1;++p) if(v[i][p])
for(int j=0;j<=k+1;++j)
inc(res.v[i][j],mul(v[i][p],rhs.v[p][j]));
return res;
}
void init()
{
for(int i=0;i<=k;++i)
{
v[i][0]=1;
for(int j=1;j<=i;++j)
v[i][j]=add(v[i-1][j],v[i-1][j-1]);
}
memcpy(v[k+1],v[k],sizeof v[k+1]);
v[k+1][k+1]=2;
}
}st,trans;
Matrix fpow(Matrix a,ll b)
{
Matrix res;
for(int i=0;i<=k+1;++i)
res.v[i][i]=1;
while(b)
{
if(b&1LL)
res=res*a;
a=a*a;
b>>=1;
}
return res;
}
ll n;
int main()
{
n=read(),k=read();
if(n==1)
return puts("1"),0;
trans.init();
for(int i=0;i<=k+1;++i)
st.v[i][0]=1;
st=fpow(trans,n-2)*st;
int ans=st.v[k+1][0];
inc(ans,fpow(n%P,k));
cout<<ans<<endl;
return 0;
}