【BZOJ5015】[Snoi2017]禮物 矩陣乘法
阿新 • • 發佈:2017-09-10
del limit 編號 load 合數 向量 個數 zoj 題解
第一行,兩個整數 N,K
N≤10^18,K≤10
【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.pdfInput
Output
一個整數,表示第 N 個朋友送的禮物個數 mod1000000007。Sample Input
4 2Sample 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]禮物 矩陣乘法