JZOJ5228. 【GDOI2018模拟7.14】小奇的集合

Description

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

分析

通过贪心策略,我们就知道所选的a、b一定是集合里面最大的两个(a>b)。

考虑最普遍的情况:
a>b>0
对于当前情况,加入的数就为:a+b,
之后最大值就变为了a+b,a
通过矩阵乘法快速幂来优化。

然而还是有些特殊情况:
一、b<0 a>0
我们就想让它变为最普遍的情况(a>0,b>0)
最简单的做法,让b不断加a。

二、b

code

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#define N 500003
#define ll long long
#define mo 10000007
using namespace std;
int n,m,a[N],mx1,mx2,k;
ll f[4][4],t[4][4],g[4][4],s;
char ch;
void read(int &n)
{
    n=0;
    ch=getchar();
    while((ch<'0' || ch>'9') && ch!='-')ch=getchar();
    int w=1;
    if(ch=='-')w=-1,ch=getchar();
    while('0'<=ch && ch<='9')n=n*10+ch-'0',ch=getchar();
    n*=w;
}
void ksm(int x)
{
    while(x)
    {
        if(x%2)
        {
            memset(t,0,sizeof(t));
            for(int i=0;i<3;i++)
                for(int j=0;j<3;j++)
                    for(int k=0;k<3;k++)
                        t[i][j]=(t[i][j]+f[i][k]*g[k][j]%mo)%mo;
            memcpy(f,t,sizeof(f));
        }
        memset(t,0,sizeof(t));
        for(int i=0;i<3;i++)
            for(int j=0;j<3;j++)
                for(int k=0;k<3;k++)
                    t[i][j]=(t[i][j]+g[i][k]*g[k][j]%mo)%mo;
        memcpy(g,t,sizeof(g));
        x/=2;
    }
}
int main()
{
    read(n);read(k);
    mx1=mx2=-2147483647;
    for(int i=1;i<=n;i++)
    {
        read(a[i]);
        s=(s+a[i]+mo)%mo;
        if(a[i]>mx1)
        {
            mx2=mx1;
            mx1=a[i];
        }
        else
            if(a[i]>mx2)mx2=a[i];
    }
    if(mx2>=0)
    {
        f[0][0]=f[1][1]=f[2][2]=1;
        g[0][1]=g[1][0]=g[2][0]=g[2][1]=g[2][2]=g[1][1]=1;
        ksm(k);
        s=(s+f[2][0]*mx2%mo+f[2][1]*mx1%mo)%mo;
    }else
    if(mx2<0 && mx1>0)
    {
        while(k && mx2<0)mx2+=mx1,k--,s=(s+mx2)%mo;
        f[0][0]=f[1][1]=f[2][2]=1;
        g[0][1]=g[1][0]=g[2][0]=g[2][1]=g[2][2]=g[1][1]=1;
        ksm(k);
        s=(s+f[2][0]*mx2%mo+f[2][1]*mx1%mo)%mo;
    }
    else
    {
        while(mx2<0)mx1+=mo,mx2+=mo;
        while(mx1<0)mx1+=mo;
        s=(s+(mx1+mx2)%mo*k)%mo; 
    }
    printf("%lld",s);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值