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);
}