BZOJ4547: Hdu5171 小奇的集合

【传送门:BZOJ4547


简要题意:

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


题解:

  我们先来看看,因为我们要得到最大的S,所以每次我们都要使得a+b最大

  首先排除一开始得到的a,b为负数的情况,我们将a+b放进S里,那么下一次操作就会将a+(a+b)(假设a>b)放进S里,再下一次就会将(a+b)+(a+b+a)放进S里,这样子就相当于一个斐波那契数列,我们就可以用矩阵乘法来求值

  但是数据保证和的最大值为非负数,就说明a一定为正数,有可能b为负数

  那么我们就暴力操作直到将b变为非负数即可

  WA了半版,发现原来不能用负数来取mod,要+mod数再取mod,气死我了


参考代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
int Mod=10000007;
struct node
{
    LL a[4][4];
    node()
    {
        memset(a,0,sizeof(a));
    }
}d,cmd;
node chengfa(node a,node b)
{
    node c;
    for(int i=1;i<=3;i++)
    {
        for(int j=1;j<=3;j++)
        {
            for(int k=1;k<=3;k++)
            {
                c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%Mod;
            }
        }
    }
    return c;
}
node p_mod(node a,int b)
{
    node ans;
    ans.a[1][1]=1;ans.a[2][2]=1;ans.a[3][3]=1;
    while(b!=0)
    {
        if(b%2==1) ans=chengfa(ans,a);
        a=chengfa(a,a);
        b/=2;
    }
    return ans;
}
LL a[110000];
int main()
{
    LL n,k;
    scanf("%lld%lld",&n,&k);
    LL sum=0;
    LL max1=-1<<31,max2=-1<<31;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        sum=(sum+a[i]+Mod)%Mod;
        if(a[i]>max1)
        {
            max2=max1;
            max1=a[i];
        }
        else if(a[i]>max2) max2=a[i];
    }
    if(k==0){printf("%lld\n",sum);return 0;}
    while(max2<0)
    {
        max2=max2+max1;
        sum=(sum+max2)%Mod;
        k--;if(k==0){printf("%lld\n",sum);return 0;}
    }
    d.a[1][1]=max1;d.a[1][2]=max2;d.a[1][3]=sum;
    cmd.a[1][1]=1;cmd.a[2][1]=1;
    cmd.a[1][2]=1;
    cmd.a[1][3]=1;cmd.a[2][3]=1;cmd.a[3][3]=1;
    d=chengfa(d,p_mod(cmd,k));
    printf("%lld\n",d.a[1][3]);
    return 0;
}

 

posted @ 2018-03-14 13:19  Star_Feel  阅读(130)  评论(0编辑  收藏  举报