【BZOJ4547】小奇的集合

Description

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

Solution

很显然,我们每次肯定是取集合中最大的两个数,那么我们设这两个数为 a b,当前的和为 s ,显然有转移:a=b,b=a+b,s=a+b+s,那么用矩阵乘法加速即可。

但是题目有负数,也就是说 a 可能小于0,所以我们先人为的把a变成正数,然后再做矩阵乘法即可。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define mo 10000007
#define ll long long
#define N 100010
using namespace std;
int a[N];
ll r[4],z[4];
ll zy[4][4]={
    {0},
    {0,0,1,1},
    {0,1,1,1},
    {0,0,0,1}
};
ll c[4][4],b[4][4];
void mul(ll p[4][4],ll q[4][4])
{
    memset(c,0,sizeof(c));
    fo(i,1,3)
    fo(j,1,3)
    fo(k,1,3) c[i][j]=(c[i][j]+p[i][k]*q[k][j]%mo)%mo;
    memcpy(p,c,sizeof(c));
}
void pow(int t)
{
    fo(i,1,3) b[i][i]=1;
    while(t)
    {
        if(t%2) mul(b,zy);
        t/=2;
        mul(zy,zy);
    }
}
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    ll s=0;
    fo(i,1,n) scanf("%d",&a[i]),s+=a[i];
    sort(a+1,a+n+1);
    r[2]=a[n];
    r[1]=a[n-1];
    if(r[1]<0 && m) r[1]=a[n]+a[n-1],r[2]=a[n],r[3]+=r[1],m--;
    pow(m);
    fo(j,1,3)
    fo(k,1,3) z[j]=(z[j]+r[k]*b[k][j]%mo)%mo;
    printf("%lld",(z[3]+s%mo+mo)%mo);
}
posted @ 2017-07-14 21:19  sadstone  阅读(40)  评论(0编辑  收藏  举报