【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

题解
显然矩阵乘法,关键在于i^k。
我们知道(i+1)^k分解后系数为杨辉三角数,因此我们可以利用i^k,i^(k-1)……i^0推导出(i+1)^k……(i+1)^0

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<set>
#include<ctime>
#include<vector>
#include<cmath>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#define mod 1000000007
#define ll long long 
#define N 25
#define inf 0x7fffffff
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
ll n;
int k,ans[N][N],a[N][N],b[N][N],fac[N];
void mul(int a[N][N],int b[N][N],int c[N][N])
{
    int tmp[N][N];
    for (int i=1;i<=k;i++)
        for (int j=1;j<=k;j++)
        {
            tmp[i][j]=0;
            for (int x=1;x<=k;x++)
                tmp[i][j]=(tmp[i][j]+(ll)a[i][x]*b[x][j]%mod)%mod;
        }
    for (int i=1;i<=k;i++)
        for (int j=1;j<=k;j++)
            c[i][j]=tmp[i][j];
}
int main()
{
    scanf("%lld",&n);k=read()+1;n-=2;
    fac[1]=1;
    for (int i=2;i<=k;i++) fac[i]=fac[i-1]*2%mod;fac[k+1]=1;
    a[1][1]=1;
    for (int i=2;i<=k;i++)
        for (int j=1;j<=i;j++)
            a[i][j]=(a[i-1][j]+a[i-1][j-1])%mod;
    k++;a[k][k-1]=1;a[k][k]=2;
    for (int i=1;i<=k;i++)
        for (int j=1;j<=k;j++) b[i][j]=a[i][j];
    for (int i=1;i<=k;i++) ans[i][i]=1;
    while (n)
    {
        if (n&1) mul(ans,a,ans);
        n>>=1;
        mul(a,a,a);
    }
    int ANS1=0;
    for (int i=1;i<=k;i++) ANS1=(ANS1+(ll)fac[i]*ans[k][i]%mod)%mod;
    mul(ans,b,ans);
    int ANS2=0;
    for (int i=1;i<=k;i++) ANS2=(ANS2+(ll)fac[i]*ans[k][i]%mod)%mod;
    printf("%d",(ANS2-ANS1+mod)%mod);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值