SNOI2017 礼物

传送门
F i F_i Fi为前 i i i项的和。那么有:
F i = 2 ∗ F i − 1 + i k F_i=2*F_{i-1}+i^k Fi=2Fi1+ik
我们考虑如何让后面可以用矩阵转移!
由于有二项式定理:
i k = ∑ t = 0 k ( k t ) ( i − 1 ) t i^k=\sum_{t=0}^{k} \binom{k}{t}(i-1)^t ik=t=0k(tk)(i1)t
那么可以把原式写成:
F i = 2 ∗ F i − 1 + ( k 0 ) ( i − 1 ) 0 + ( k 1 ) ( i − 1 ) 1 + ⋯ + ( k k ) ( i − 1 ) k F_i=2*F_{i-1}+\binom{k}{0}(i-1)^0+\binom{k}{1}(i-1)^1+\cdots+\binom{k}{k}(i-1)^k Fi=2Fi1+(0k)(i1)0+(1k)(i1)1++(kk)(i1)k

于是可以构造出

[ 2 ( k 0 ) ( k 1 ) ( k 2 ) ⋯ ( k k ) 0 ( 0 0 ) 0 0 0 0 0 ( 1 0 ) ( 1 1 ) 0 ⋯ 0 ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ 0 ( k 0 ) ( k 1 ) ( k 2 ) ⋯ ( k k ) ] [ F i − 1 ( i − 1 ) 0 ( i − 1 ) 1 ⋮ ( i − 1 ) k ] = [ F i i 0 i 1 ⋮ i k ] \left[ \begin{matrix} 2 & \binom{k}{0} & \binom{k}{1} & \binom{k}{2} & \cdots & \binom{k}{k}\\ \\ 0 & \binom{0}{0} & 0 & 0 & 0 & 0 \\ \\ 0 & \binom{1}{0} & \binom{1}{1} & 0 & \cdots &0\\ \\ \vdots & \vdots & \vdots & \vdots & \vdots& \vdots\\ \\ 0 & \binom{k}{0} & \binom{k}{1} & \binom{k}{2} & \cdots &\binom{k}{k}\\ \end{matrix} \right] \left[ \begin{matrix} F_{i-1} \\ (i-1)^0\\ (i-1)^1\\ \vdots\\ (i-1)^k\\ \end{matrix} \right]= \left[ \begin{matrix} F_i \\ i^0 \\ i^1 \\ \vdots \\ i^k \\ \end{matrix} \right] 2000(0k)(00)(01)(0k)(1k)0(11)(1k)(2k)00(2k)0(kk)00(kk)Fi1(i1)0(i1)1(i1)k=Fii0i1ik
这些系数也是根据二项式定理得出的,于是这题就没了。注意 n n n读进来是 l o n g   l o n g long\ long long long。矩阵快速幂算出来的是前 n n n项的和,于是我们算出前 n − 1 n-1 n1项的和(只用乘 n − 2 n-2 n2次)再加上当前位置的贡献即可。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7,N=12;
int k,fac[N],ifac[N];ll n;
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline int quickpow(int a,int b,int ret=1){for(;b;b>>=1,a=mul(a,a))if(b&1)ret=mul(ret,a);return ret;}
inline void Add(int &x,int y){x=(x+y>=mod)?x+y-mod:x+y;}
inline int C(int n,int m){return n<m?0:mul(fac[n],mul(ifac[m],ifac[n-m]));}
struct matrix{
	int a[N][N];
	matrix(int t=0){
		memset(a,0,sizeof(a));
		for(int i=0;i<N;++i) a[i][i]=t;
	}
	inline void init(int k){
		a[0][0]=2;
		for(int j=1;j<=k+1;++j) a[0][j]=C(k,j-1);
		for(int i=1;i<=k+1;++i)
			for(int j=1;j<=i;++j)
				a[i][j]=C(i-1,j-1);
	}
	inline int getval(int ret=0){
		for(int i=0;i<=k+1;++i) Add(ret,a[0][i]);
		return ret;
	}
	friend inline matrix operator*(const matrix &a,const matrix &b){
		matrix c;
		for(int i=0;i<=k+1;++i)
			for(int l=0;l<=k+1;++l)
				for(int j=0;j<=k+1;++j)
					Add(c.a[i][j],mul(a.a[i][l],b.a[l][j]));
		return c;
	}
	friend inline matrix operator^(matrix a,ll b){
		matrix c(1);
		for(;b;b>>=1,a=a*a) if(b&1) c=c*a;
		return c;
	}
}A;
inline void prework(){
	fac[0]=1;
	for(int i=1;i<N;++i) fac[i]=mul(fac[i-1],i);
	ifac[N-1]=quickpow(fac[N-1],mod-2);
	for(int i=N-2;~i;--i) ifac[i]=mul(ifac[i+1],i+1);
}
int main(){
	cin>>n>>k,prework(),A.init(k);
	if(n==1) return puts("1"),0;
	printf("%d\n",add(quickpow(n%mod,k),(A^(n-2)).getval()));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值