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 + Sk * 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\n" , res);
}
return 0;
}
另外记录一个斐波那契的通项公式: ,在HDU1568中会用这个公式。