【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; }