【BZOJ5015】[Snoi2017]礼物 矩阵乘法

【BZOJ5015】[Snoi2017]礼物

Description

热情好客的请森林中的朋友们吃饭,他的朋友被编号为 1~N,每个到来的朋友都会带给他一些礼物:。其中,第一个朋友会带给他 1 个,之后,每一个朋友到来以后,都会带给他之前所有人带来的礼物个数再加他的编号的 K 次方那么多个。所以,假设 K=2,前几位朋友带来的礼物个数分别是:1,5,15,37,83假设 K=3,前几位朋友带来的礼物个数分别是:1,9,37,111现在,好奇自己到底能收到第 N 个朋友多少礼物,因此拜托于你了。已知 N,K请输出第 N 个朋友送的礼物个数 mod1000000007。

Input

第一行,两个整数 N,K
N≤10^18,K≤10

Output

一个整数,表示第 N 个朋友送的礼物个数 mod1000000007。 

Sample Input

4 2

Sample Output

37

题解:一开始想到$1^d+2^d+...+n^d$是一个d+1次的多项式,所以猜想它的前缀和也是一个多项式,后来实验了一下死活试不出来。于是换个思路想矩乘,倒是一下就想出来了。。。

我们用S[i]表示$sumlimits_{i=1}^nsumlimits_{j=1}^ij^k=sumlimits_{i=1}^ni^k(n-i+1)$,那么ans=S[n]-S[n-1],所以维护以下行向量:

$egin{pmatrix}i^0 & i^1 & ... & i^k & S[i] $

如何得到i^k呢?用$(i-1)^0,(i-1)^1,...(i-1)^k$乘上组合数即可。如何得到S[i]呢?用$2*S[i-1]+i^k$即可。然后就没了。

 

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const ll P=1000000007;
ll n;
ll c[20][20];
int m;
struct M
{
	ll a[20][20];
	M () {memset(a,0,sizeof(a));}
	ll * operator [] (int b){return a[b];}
	M operator * (M b)
	{
		M c;
		int i,j,k;
		for(i=0;i<=m+1;i++)	for(j=0;j<=m+1;j++)	for(k=0;k<=m+1;k++)	c[i][j]=(c[i][j]+a[i][k]*b[k][j])%P;
		return c;
	}
}tr,a1,a2;
M pm(M ret,ll y)
{
	M x=tr;
	while(y)
	{
		if(y&1)	ret=ret*x;
		x=x*x,y>>=1;
	}
	return ret;
}
int main()
{
	scanf("%lld%d",&n,&m);
	int i,j;
	c[0][0]=1;
	for(i=1;i<=m;i++)
	{
		c[i][0]=1;
		for(j=1;j<=i;j++)	c[i][j]=(c[i-1][j-1]+c[i-1][j])%P;
	}
	for(i=0;i<=m;i++)
	{
		a1[0][i]=a2[0][i]=1;
		for(j=0;j<=i;j++)	tr[j][i]=c[i][j];
	}
	tr[m+1][m+1]=2,tr[m][m+1]=1;
	a1=pm(a1,n-1),a2=pm(a2,n);
	printf("%lld",(a2[0][m+1]-a1[0][m+1]+P)%P);
	return 0;
}

 

原文地址:https://www.cnblogs.com/CQzhangyu/p/7500294.html