有一个大小为n的可重集S,小奇每次操作可以加入一个数a+b(a,b均属于S),求k次操作后它可获得的S的和的最大
值。(数据保证这个值为非负数)
有一个大小为n的可重集S,小奇每次操作可以加入一个数a+b(a,b均属于S),求k次操作后它可获得的S的和的最大
第一行有两个整数n,k表示初始元素数量和操作数,第二行包含n个整数表示初始时可重集的元素。
By Hzwer
题解:矩阵乘法+递推
发现每次都是从集合中选取最大的两个数把他们的和加入集合中, 那么以样例为例
3,6, 3+6,3+6+6,3+3+6+6+6.......可以发现最大值,和次大值的在每项中的系数满足斐波那契数列.
于是就牵扯到了斐波那契数列求前缀和.
貌似直接构造矩阵求前缀和亦可.
#include
#include
#include
#include
#include
#define p 10000007
#define N 100005
#define LL long long
using namespace std;
int n,m,k,m1,m2;
LL a[N*2],ans;
struct data
{
LL x[10][10];
}num,e,t,t1;
void clear(data &a)
{
for (int i=1;i<=k;i++)
for (int j=1;j<=k;j++)
a.x[i][j]=0;
}
void change(data &a,data b)
{
for (int i=1;i<=k;i++)
for (int j=1;j<=k;j++)
a.x[i][j]=b.x[i][j];
}
data mul(data a,data b)
{
data c;
for(int i=1;i<=k;i++)
for (int j=1;j<=k;j++)
{
c.x[i][j]=0;
for (int t=1;t<=k;t++)
c.x[i][j]=(c.x[i][j]+a.x[i][t]*b.x[t][j]%p)%p;
}
return c;
}
data pow(data num,int x)
{
data ans; clear(ans);
for (int i=1;i<=k;i++) ans.x[i][i]=1;
data base;
change(base,num);
while(x)
{
if (x&1) ans=mul(ans,base);
x>>=1;
base=mul(base,base);
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%lld",&a[i]),ans=(ans+a[i])%p;
sort(a+1,a+n+1);
while (a[n-1]<0)
{
a[n-1]+=a[n]; ans=(ans+a[n-1])%p;
m--;
}
if (a[n-1]>a[n]) swap(a[n-1],a[n]);
k=2;
clear(num);
num.x[1][1]=1; num.x[1][2]=1;
num.x[2][1]=1; num.x[2][2]=0;
if (m%2) {
m1=m+1;
m2=m;
}
else
{
m1=m;
m2=m+1;
}
data b=pow(num,m1);
data c=pow(num,m2);
ans=(ans+a[n-1]%p*((b.x[1][2]+c.x[1][2]-1)%p))%p;
m++;
if (m%2) {
m1=m+1;
m2=m;
}
else
{
m1=m;
m2=m+1;
}
b=pow(num,m1);
c=pow(num,m2);
ans=(ans+a[n]%p*((b.x[1][2]+c.x[1][2]-2)%p))%p;
printf("%lld\n",(ans+p)%p);
}