[bzoj4547][矩阵乘法]小奇的集合

本文介绍了一种解决特定数学问题的算法:如何通过有限次操作最大化一个可重集中所有元素的总和。该算法首先对集合中的元素进行排序,接着采用矩阵快速幂的方法来高效计算最优操作序列,并考虑了特殊情况下的处理策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

有一个大小为n的可重集S,小奇每次操作可以加入一个数a+b(a,b均属于S),求k次操作后它可获得的S的和的最大

值。(数据保证这个值为非负数)

Input

第一行有两个整数n,k表示初始元素数量和操作数,第二行包含n个整数表示初始时可重集的元素。

对于100%的数据,有 n<=10^5,k<=10^9,|ai|<=10^5

Output

输出一个整数,表示和的最大值。答案对10000007取模。

Sample Input

2 2

3 6

Sample Output

33

题解

把数集从小到大排序,简单思考一下肯定是每次加入a[n]+a[n-1]最优。
然后稍微推一下可以发现,a[n]与a[n-1]对于k来说,前面的系数都是斐波那契数列里的数
写个矩乘
还没完,假如a[n]>0,a[n-1]<0怎么办,那么每次加入a[n]+a[n-1]就不是最优了
我们暴力出a[n]*k+a[n-1]>=0中k的最小值,代表每次都加入这个。由于abs(a[n])<=10^5,这个暴力最大的复杂度是10^5了
暴力完之后,再用暴力出来的数与a[n]作为新数加入数列中。
由于这题保证答案大于0所以不需要考虑a[n]<0的情况,虽然我考虑了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const LL mod=10000007;
struct matrix
{
    LL m[5][5];
    matrix(){memset(m,0,sizeof(m));}
}st,tmp,stx,sty;
matrix multi(matrix u,matrix v,int n,int m,int p)
{
    matrix ret;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            for(int k=1;k<=p;k++)
                ret.m[i][k]=(ret.m[i][k]+u.m[i][j]*v.m[j][k])%mod;
    return ret;
}
matrix pow_mod(matrix u,int b)
{
    matrix ans;
    for(int i=1;i<=4;i++)ans.m[i][i]=1;
    while(b)
    {
        if(b%2==1)ans=multi(ans,u,4,4,4);
        u=multi(u,u,4,4,4);b/=2;
    }
    return ans;
}
int pow_(int a,int b)
{
    int ans=1;
    while(b)
    {
        if(b%2==1)ans=(ans*a)%mod;
        a=(a*a)%mod;b/=2;
    }
    return ans;
}
int n,k;LL a[110000],sum;
bool check(int kk)
{   
    sum=a[n-1];
    sum+=a[n]*kk;
    if(sum>=0)return true;
    return false;
}
int main()
{
    scanf("%d%d",&n,&k);
    int bk=0;
    for(int i=1;i<=n;i++){scanf("%lld",&a[i]);if(a[i]>0)bk++;}
    sort(a+1,a+1+n);
    if(bk==0)
    {
        LL ans=0;
        for(int i=1;i<=n;i++)ans=(ans+a[i])%mod;
        ans=(ans+pow_(a[n-1],k))%mod;ans=(ans+pow_(a[n],k))%mod;
        printf("%lld\n",ans);
    }
    else
    {
        if(k==0)
        {
            LL ans=0;
            for(int i=1;i<=n;i++)ans=(ans+a[i])%mod;
            printf("%lld\n",ans);
            return 0;
        }
        if(bk>=2)
        {
            st.m[1][2]=st.m[2][1]=st.m[2][2]=st.m[3][4]=st.m[4][2]=st.m[4][4]=1;
            tmp.m[1][1]=1*a[n-1];tmp.m[2][1]=1*a[n-1];tmp.m[3][1]=0;tmp.m[4][1]=1*a[n-1];
            matrix p=multi(pow_mod(st,k-1),tmp,4,4,1);

            tmp.m[1][1]=1*a[n];tmp.m[2][1]=2*a[n];tmp.m[3][1]=0;tmp.m[4][1]=1*a[n];
            matrix q=multi(pow_mod(st,k-1),tmp,4,4,1);
            LL ans=0;
            for(int i=1;i<=n;i++)ans=(ans+a[i])%mod;
            ans=(ans+q.m[4][1])%mod;ans=(ans+p.m[4][1])%mod;
            printf("%lld\n",ans);
        }
        if(bk==1)
        {
            LL ans=0;
            for(int i=1;i<=n;i++)ans=(ans+a[i])%mod;
            int kk=1,kt=a[n-1]+a[n];ans+=kt;
            while(kt<0){kt+=a[n];ans+=kt;kk++;}
            a[n-1]=min((LL)kt,a[n]);a[n]=max((LL)kt,a[n]);
            st.m[1][2]=st.m[2][1]=st.m[2][2]=st.m[3][4]=st.m[4][2]=st.m[4][4]=1;
            tmp.m[1][1]=1*a[n-1];tmp.m[2][1]=1*a[n-1];tmp.m[3][1]=0;tmp.m[4][1]=1*a[n-1];
            matrix p=multi(pow_mod(st,k-kk-1),tmp,4,4,1);

            tmp.m[1][1]=1*a[n];tmp.m[2][1]=2*a[n];tmp.m[3][1]=0;tmp.m[4][1]=1*a[n];
            matrix q=multi(pow_mod(st,k-kk-1),tmp,4,4,1);
            ans=(ans+q.m[4][1])%mod;ans=(ans+p.m[4][1])%mod;
            printf("%lld\n",ans);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值