bzoj 5015: [Snoi2017]礼物

题意

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

题解

这题的话,n为 1018 ,然后还一副递推式很显然的样子,一看就是矩阵乘法
递推式的话也是很显然的,设前i个人的答案总和 fi
fi=2fi1+ik2
然后答案就是 fnfn1
然后难点就是 ik 怎么矩乘维护
明显地,直接弄是弄不出来的,你需要 (i1)k1 , (i1)k2 等等
然后你知道了这些之后,其实合并的话,就是杨辉三角
因为你就相当于一个 (n+1)k 形式而已,拆开之后,各项的系数就是杨辉三角了

然后矩乘搞一搞就好了

CODE:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long LL;
const int MOD=1000000007;
const LL N=25;
LL n,k;
LL C[N][N];
struct qq{LL s[20][20];};
qq operator * (qq a,qq b)
{
    qq c;
    for (LL u=0;u<=k+1;u++)
        for (LL i=0;i<=k+1;i++)
        {
            c.s[u][i]=0;
            for (LL j=0;j<=k+1;j++)
                c.s[u][i]=(c.s[u][i]+a.s[u][j]*b.s[j][i]%MOD)%MOD;
        }
    return c;
}
qq a,b,c,d,e;
qq pow (qq x,LL y)
{
    if (y==0) return e;
    if (y==1) return x;
    qq lalal=pow(x,y>>1);
    lalal=lalal*lalal;
    if (y&1) lalal=lalal*x;
    return lalal;
}
int main()
{
    scanf("%lld%lld",&n,&k);
    C[0][0]=1;
    for (LL u=1;u<=k;u++)
    {
        C[u][0]=1;
        for (LL i=1;i<=u;i++)   C[u][i]=(C[u-1][i]+C[u-1][i-1])%MOD;
    }
    a.s[k+1][k]=1;a.s[k+1][k+1]=2;
    for (LL u=0;u<=k;u++)
    {
        b.s[u][0]=1;
        e.s[u][u]=1;
        for (LL i=0;i<=u;i++)   a.s[u][i]=C[u][i];
    }
    e.s[k+1][k+1]=1;
    c=pow(a,n);d=pow(a,n-1);
    c=c*b;d=d*b;
    printf("%lld\n",((c.s[k+1][0]-d.s[k+1][0])%MOD+MOD)%MOD);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值