题目及代码:题目大意就是给定一个含有n个元素的序列,进行k次操作---每次操作从序列里面选择两个数,求和并将结果放在这个序列中,使得k次操作后这个序列中的元素和最大。
当然为了满足最大这个要求,那么我们每次当然是从序列中选择最大的两个元素相加,结果放在这个序列中,这个结果比为序列元素的最大元素。这样我们只需要找到原序列中最大的两个元素,累加就可以了。简单化简一下就知道这是一个经典的菲波那切数列求和,由于k很大,使用矩阵来加速就可以了,注意一下细节的处理,具体见代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
const ll mod=10000007;
struct mat
{
ll t[4][4];
void set()
{
memset(t,0,sizeof(t));
}
}a,b;
mat multiple(mat a,mat b,ll n,ll p)
{
ll i,j,k;
mat temp;
temp.set();
for(i=0; i<n; i++)
for(j=0; j<n; j++)
{
if(a.t[i][j]!=0)
for(k=0; k<n; k++)
temp.t[i][k]=(temp.t[i][k]+a.t[i][j]*b.t[j][k])%p;
}
return temp;
}
mat quick_mod(mat b,ll n,ll m,ll p)
{
mat t;
t.set();
for(ll i=0; i<n; i++) t.t[i][i]=1;
while(m)
{
if(m&1)
{
t=multiple(t,b,n,p);
}
m>>=1;
b=multiple(b,b,n,p);
}
return t;
}
void init()
{
b.set();
b.t[0][1]=b.t[0][2]=1;
b.t[1][0]=b.t[1][1]=1;
b.t[1][2]=b.t[2][2]=1;
}
int main()
{
ll n,k;
while(scanf("%I64d%I64d",&n,&k)!=EOF)
{
ll sum=0,x=0,y=0,z=0,A=0,B=0;
for(int i=0;i<n;i++)
{
scanf("%I64d",&x);
sum=(sum+x)%mod;
if(x>B) B=x;
if(A<B) swap(A,B);
}
if(k==1)
{
sum=(sum+A+B)%mod;
printf("%I64d\n",sum);
continue;
}
if(k==2)
{
sum=(sum+A*3+B*2)%mod;
printf("%I64d\n",sum);
continue;
}
init();
a=quick_mod(b,3,k-2,mod);
z=a.t[0][2]+a.t[1][2]+2*a.t[2][2];
sum=(sum+(z%mod)*(B%mod)%mod)%mod;
z=a.t[0][2]+2*a.t[1][2]+3*a.t[2][2];
sum=(sum+(z%mod)*(A%mod)%mod)%mod;
printf("%I64d\n",sum);
}
return 0;
}