向前走莫回头❤

【bzoj 4547】【Hdu 5171】小奇的集合(找规律+矩阵快速幂)

4547: Hdu5171 小奇的集合

Time Limit: 2 Sec Memory Limit: 256 MB
Submit: 184 Solved: 90
[Submit][Status][Discuss]
Description

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

Input
第一行有两个整数n,k表示初始元素数量和操作数,第二行包含n个整数表示初始时可重集的元素。
对于100%的数据,有 n<=10^5,k<=10^9,|ai|<=10^5

Output
输出一个整数,表示和的最大值。答案对10000007取模。

Sample Input
2 2
3 6

Sample Output
33

【题解】【找规律+矩阵快速幂】
【本题需要分两种情况考虑:1)序列的最大值和次大值都是非负整数;2)序列的次大值是负数】
【对于第二种情况,要先暴力地用最大值把次大值更新成非负整数并记录所需的次数t,若k<t,则要提前退出、输出】
【若k>t,则k-=t。剩下的部分处理方式和第一种情况相同】
【由题意可知,设原序列为:a1,a2,a3,a4an1,an
1)a1,a2,a3,a4an1,an,an1+an
2)a1,a2,a3,a4an1,an,an1+anan1+2an
3)a1,a2,a3,a4an1,an,an1+anan1+2an2an1+3an
……
【通过以上的列举,可以发现这是斐波那契数列,可以用矩阵乘法优化】
{1,0,0}
{1,1,1} * {Sn1fnfn1} ={Sn,fn+1,fn}
{0,1,0}

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int mod=10000007;
struct unite{
    ll k[3][3];
}d,f;
ll  ans;
int n,m,h[100010],t;
int tmp(int a,int b)
{
    return a>b;
}
inline unite jc(unite a,unite b)
{
    int i,j,l;
    unite c;
    for(i=0;i<=2;++i)
     for(j=0;j<=2;++j)
      c.k[i][j]=0;
    for(i=0;i<=2;++i)
     for(j=0;j<=2;++j)
      for(l=0;l<=2;++l)
        c.k[i][j]=(c.k[i][j]+a.k[i][l]*b.k[l][j]%mod)%mod;
    return c;
}
inline unite poww(unite d,int p)
{
    unite sum;
    for(int i=0;i<=2;++i)
     for(int j=0;j<=2;++j)
      sum.k[i][j]=0;
    sum.k[0][0]=sum.k[1][1]=sum.k[2][2]=1;
    for(;p;p>>=1,d=jc(d,d))
     if(p&1)
      sum=jc(sum,d);
    return sum;
}
int main()
{
    freopen("set.in","r",stdin);
    freopen("set.out","w",stdout);
    int i,j;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;++i) scanf("%d",&h[i]),ans+=h[i],ans%=mod;
    if(!m) {printf("%I64d\n",ans); return 0;}
    sort(h+1,h+n+1,tmp);
    while(h[2]<0&&t<m) h[2]+=h[1],t++,ans+=h[2],ans%=mod;
    if(t==m) {printf("%I64d\n",ans); return 0;}
    d.k[0][0]=d.k[1][0]=d.k[1][1]=d.k[2][1]=d.k[1][2]=1;
    d=poww(d,m-t-1);
    f.k[0][0]=f.k[0][1]=f.k[0][2]=1;
    f=jc(f,d);
    ll t1=f.k[0][0],t2=(f.k[0][0]-1+f.k[0][1])%mod; 
    ans=(ans+(h[2]*t1%mod+h[1]*t2%mod)%mod)%mod;
    ans=(ans+mod)%mod;
    printf("%lld\n",ans);
    return 0;
}
posted @ 2016-08-18 08:31  lris0-0  阅读(88)  评论(0编辑  收藏  举报
过去的终会化为美满的财富~o( =∩ω∩= )m