SNOI2017 礼物

题解

设前\(n\)个人的礼物个数为\(F_n\), 那么显然\[F_n = 2 \times F_{n-1} + i^k\]
考虑矩阵快速幂
棘手的问题是:\(i^k\)不是可以直接用矩阵乘法可以递推的东西
由二项式定理可得:\[a^k = \sum_{i = 1}^{k}(a-1)^i {k \choose i}\]
那么我们可以给\(\left( (i-1)^0\; (i-1)^1\;(i-1)^2\; \cdots\; (i-1)^k\;\right)\) 乘上一个杨辉三角矩阵, 就能得到\(\left(i^0\;i^1\;i^2\;\cdots\;i^k\right)\)
然后我们就能用矩阵快速幂算\(F\), 显然\(Ans = F_{n-1} + i^k\)
于是就做完了

注意:这题两个n相乘可能会long long溢出, 因此算\(n^k\)前要先给\(n\)取模

代码

#include 
#include 
#include 
#include 
#include 

using namespace std;


typedef long long LL;


const LL mod = 1e9 + 7;


LL power(LL a, LL n, LL mod)
{
    LL Ans = 1;
    
    while (n)
    {
        if (n & 1) Ans = Ans * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    
    return Ans;
}


LL n; int K;


struct Matrix
{
    LL a[12][12];

    Matrix() { memset(a, 0, sizeof(a)); }

    LL* operator [] (int x) { return a[x]; }
    
    friend Matrix operator * (Matrix a, Matrix b)
    {
        Matrix Ans;
        
        for (int i = 0; i <= K + 1; i++)
            for (int k = 0; k <= K + 1; k++)
                if (a[i][k])
                    for (int j = 0; j <= K + 1; j++)
                        (Ans[i][j] += a[i][k] * b[k][j] % mod) %= mod;
        
        return Ans;
    }
};

Matrix power(Matrix a, LL n)
{
    Matrix Ans;
    for (int i = 0; i <= K + 1; i++) Ans[i][i] = 1;
    
    while (n)
    {
        if (n & 1) Ans = Ans * a;
        a = a * a;
        n >>= 1;
    }
    
    return Ans;
}


Matrix Ans, a;


int main()
{
    Matrix A, Ans;
    
    scanf("%lld %d", &n, &K);
    
    if (n <= 2)
    {
        LL Ans = 1;
        for (int i = 2; i < n; i++)
            Ans = (2 * Ans + power(i, K, mod)) % mod;
        Ans = (Ans + power(n, K, mod)) % mod;
        printf("%lld\n", Ans);
        return 0;
    }
    
    A[0][0] = 1;
    for (int i = 1; i <= K; i++)
    {
        A[i][0] = 1; A[i][i] = 1;
        for (int j = 1; j < i; j++)
            A[i][j] = (A[i-1][j-1] + A[i-1][j]) % mod;
    }
    A[K+1][K+1] = 2;
    for (int i = 0; i <= K; i++)
        A[K+1][i] = A[K][i];
    
    for (int i = 0; i <= K; i++)
        Ans[i][1] = 1;
    Ans[K+1][1] = 1;
    
    Ans = power(A, n - 2) * Ans;
    
    printf("%lld\n", (Ans[K+1][1] + power(n%mod, K, mod)) % mod);
    
    return 0;
}

转载于:https://www.cnblogs.com/2016gdgzoi509/p/11148856.html

你可能感兴趣的:(SNOI2017 礼物)