loj|loj 2253 bzoj 5015 「SNOI2017」礼物


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

记得去年snoi去玩的时候似乎算出了小数据的公式,还拿了五十分,现在完全自己手推,终于可以推出正解了...
如果记当前位置的前缀和为sum[i],当前i的k次方为b[i],那么当前i答案就是sum[i]+b[i],那么下一个位置的前缀和就是当前位置的前缀和加上当前位置的值即sum[i+1]= (sum[i]+sum[i]+b[i]),下一个位置的k次方为b[i+1]=(b[i]+1)^k。sum可直接在矩阵中算出,那么为了构造b[i]+1的k次方,就需要用到二项式定理了。
二项式定理为:(a+b)^n=C(n,0)a^n+C(n,1)a^(n-1)b+...+C(n,r)a^(n-r)b^r+...+C(n,n)b^n,那么我们将我们的式子代入即为C(n,0)b[i]^n+C(n,1)b[i]^(n-1)+...+C(n,r)b[i]^(n-r)+...+C(n,n)1,有了这样的一个式子,我们可以在一般矩阵将一般矩阵构造为[sum[i],b[i]^k,b[i]^k-1,b[i]^k-1...,b[i]^0],接着特殊矩阵的第一列第一个构造为2,第二列第一个构造为1,这样可得到sum[i+1],接着从第二列开始每一列从上到下即为C(n,0)到C(n,n)了,例当k=3时,特殊矩阵如下图
loj|loj 2253 bzoj 5015 「SNOI2017」礼物
文章图片

这样求出特殊矩阵的k次方与一般矩阵相乘,答案的sum[i]+b[i]即为所求的答案了。
下附AC代码。

#include #include #include #include #define maxn 15 using namespace std; typedef long long ll; const ll mod=1e9+7; ll n,k; ll c[maxn][maxn]; struct mat { int n,m; ll val[maxn][maxn]; mat() { n=1; m=1; memset(val,0,sizeof(val)); } }a,b; mat mul(mat a,mat b) { mat ans; ans.n=a.n; ans.m=a.m; for(int i=1; i<=a.n; i++) for(int j=1; j<=a.m; j++) for(int k=1; k<=a.m; k++) { ans.val[i][j]+=a.val[i][k]*b.val[k][j]; ans.val[i][j]%=mod; } return ans; } mat pow(mat p,ll k) { mat ans; ans.n=ans.m=p.n; for(int i=1; i<=ans.n; i++) ans.val[i][i]=1; while(k) { if(k&1) ans=mul(ans,p); p=mul(p,p); k>>=1; } return ans; } int main() { scanf("%lld%lld",&n,&k); a.n=1; a.m=k+2; for(int i=2; i<=a.m; i++) a.val[1][i]=1; b.n=k+2; b.m=k+2; b.val[1][1]=2; b.val[2][1]=1; for(int j=k+2; j>=2; j--) { b.val[j][j]=1; b.val[k+2][j]=1; for(int i=k+1; i>j; i--) b.val[i][j]=b.val[i][j+1]+b.val[i+1][j+1]; } b=pow(b,n-1); mat ans=mul(a,b); printf("%lld\n",(ans.val[1][1]+ans.val[1][2])%mod); }



【loj|loj 2253 bzoj 5015 「SNOI2017」礼物】

    推荐阅读