1. 程式人生 > >【BZOJ5015】[Snoi2017]禮物 矩陣乘法

【BZOJ5015】[Snoi2017]禮物 矩陣乘法

del limit 編號 load 合數 向量 個數 zoj 題解

【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。 PDF題面:www.lydsy.com/JudgeOnline/upload/gift.pdf

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]表示$\sum\limits_{i=1}^n\sum\limits_{j=1}^ij^k=\sum\limits_{i=1}^ni^k(n-i+1)$,那麽ans=S[n]-S[n-1],所以維護以下行向量:

$\begin{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;
}

【BZOJ5015】[Snoi2017]禮物 矩陣乘法