[bzoj5015][Snoi2017]礼物

来自FallDream的博客,未经允许,请勿转载,谢谢。


热情好客的请森林中的朋友们吃饭,他的朋友被编号为 1~N,每个到来的朋友都会带给他一些礼物:。其中,第一个朋友会带给他 1 个,之后,每一个朋友到来以后,都会带给他之前所有人带来的礼物个数再加他的编号的 K 次方那么多个。所以,假设 K=2,前几位朋友带来的礼物个数分别是:1,5,15,37,83假设 K=3,前几位朋友带来的礼物个数分别是:1,9,37,111现在,好奇自己到底能收到第 N 个朋友多少礼物,因此拜托于你了。已知 N,K请输出第 N 个朋友送的礼物个数 mod1000000007
N≤10^18,K≤10
 
首先把它给你的数字前缀和一下 得到递推式f[i]=2*f[i-1]+i^k
构造一个k+2维的矩阵,一维维护答案,另外分别维护i的0到k次方数字。
转移矩阵的话,n^k -> (n+1)^k ,把后面的用组合数拆开就行了。
复杂度k^3logn
#include<iostream>
#include<cstring>
#include<cstdio>
#define mod 1000000007
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;
}
int k,C[15][15];long long n;
struct Matrix
{
    int s[12][12];
    Matrix(){memset(s,0,sizeof(s));}
    Matrix operator * (const Matrix&b)
    {
        Matrix c;    
        for(int i=0;i<=k+1;++i)
            for(int j=0;j<=k+1;++j)
                for(int K=0;K<=k+1;++K)
                    c.s[i][j]=(c.s[i][j]+1LL*s[i][K]*b.s[K][j])%mod;
        return c;
    }
    void print()
    {
        for(int i=0;i<=k+1;++i,puts(""))
            for(int j=0;j<=k+1;++j)
                printf("%d ",s[i][j]);    
    }
}A,B,c,d;

int Solve(long long N)
{
    A=c;B=d;
    for(;N;N>>=1,A=A*A) if(N&1) B=A*B;    
    return B.s[k+1][0];
}

int main()
{
    for(int i=0;i<=12;++i) C[i][0]=1;
    for(int i=1;i<=12;++i)for(int j=1;j<=i;++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod; 
    cin>>n>>k;
    A.s[k+1][k+1]=2;A.s[k+1][k]=1;A.s[0][0]=1;
    for(int i=0;i<=k;++i) 
    {
        B.s[i][0]=1;
        for(int j=0;j<=i;++j)
            A.s[i][j]=C[i][i-j];
    }    
    c=A;d=B;
    printf("%d\n",(Solve(n)-Solve(n-1)+mod)%mod);
    return 0;
}
posted @ 2017-09-06 16:43  FallDream  阅读(267)  评论(0编辑  收藏  举报