【BZOJ4547】小奇的集合

来源:互联网 发布:报名淘宝客活动有用吗 编辑:程序博客网 时间:2024/04/27 13:52

Description

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

Solution

很显然,我们每次肯定是取集合中最大的两个数,那么我们设这两个数为ab,当前的和为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 100010using 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);}
原创粉丝点击