HDU5171 矩阵快速幂

  题目描述:http://acm.hdu.edu.cn/showproblem.php?pid=5171

 


算法:  

  可以先将数组a[]排序,然后序列 a1 , a2 , … , an 即为有序序列,则第一次加入的就是 an + an-1 ,第二次就是 an + (an + an-1) ,如此循环就构成了斐波那契序列。

  设斐波那契序列的第k项为Fk,则可知第k次加入的即为 Fk+1 * an +  Fk * an-1

  设斐波那契序列的前k项和为Sk, 则所得结果res即为:a1 + a2 + … + an-1 + an + (Sk+1 - 1) * an + S* an-1

实现方法:

  由于需要对结果取余,所以可以考虑用矩阵快速幂来实现斐波那契序列,具体怎么实现可以看这里

  并且根据公式可以知道 Sk = 2 * Fk + Fk-1 - 1 , 代入上式即可。

  


 

#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL __int64
#define eps 1e-8
const int MOD = 10000007; 
const int maxn = 100000 + 5;
const int N = 2;
struct Mat {
    LL mat[N][N];
} a;
void init()
{
    a.mat[0][0] = a.mat[0][1] = a.mat[1][0] = 1;
    a.mat[1][1] = 0;
}
Mat operator *(Mat a , Mat b)
{
    Mat tmp;
    memset(tmp.mat , 0 , sizeof(tmp.mat));
    for(int i = 0 ; i < N ; i++)
        for(int j = 0 ; j < N ; j++)
            for(int k = 0 ; k < N ; k++)
                tmp.mat[i][j] = (tmp.mat[i][j] + a.mat[i][k] * b.mat[k][j]) % MOD;
    return tmp;
}
Mat operator ^(Mat a , int k)
{
    Mat tmp;
    for(int i = 0 ; i < N ; i++)
        for(int j = 0 ; j < N ; j++)
            tmp.mat[i][j] = (i == j);
    while(k) {
        if(k & 1)
            tmp = tmp * a;
        a = a * a;
        k /= 2;
    }
    return tmp;
}
LL Fib(int k)
{
    Mat tmp;
    tmp = a ^ k;
    return tmp.mat[1][0];
}
LL a_[maxn];
int main()
{
    init();
    LL n , k , i , j , res;
    while(~scanf("%lld %lld" , &n , &k))
    {
        for(i = 1 , res = 0 ; i <= n ; i++) {
            scanf("%lld" , &a_[i]);
            res += a_[i];
        }
        sort(a_ + 1 , a_ + n + 1);
        LL tmp = ((2 * Fib(k + 1) + Fib(k) + 10000005) * a_[n] + (2 * Fib(k) + Fib(k - 1) + 10000006) * a_[n - 1]) % MOD;
        res = (res + tmp) % MOD;
        printf("%lld
" , res);
    }
    return 0;
}

另外记录一个斐波那契的通项公式: ,在HDU1568中会用这个公式。

 

原文地址:https://www.cnblogs.com/H-Vking/p/4282716.html