hdu 5171-GTY's birthday gift(矩阵快速幂)

题目及代码:题目大意就是给定一个含有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;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值