【BZOJ4547】【HDU5171】小奇的集合,暴力+矩阵乘法

Time:2016.08.18
Author:xiaoyimi
转载注明出处谢谢


传送门1
传送门2
思路:
基本想法是每次找出最大的两个相加即可
而且由于题目要求,起始的n个数中至少有一个是正整数
当最大的两个数都是非负整数时,我们发现每次的求值就是一个这样的数列

fi=fi1+fi2

是不是很眼熟,很像fibonacci数列……
那我们直接构造转移矩阵,然后矩阵快速幂就可以了啊……
那求和怎么办?
我们先从 S1 入手
S1=f1=f3f2
S2 , S3 呢?
S2=f1+f2=(f3+f2)f2=f4f2
S3=f1+f2+f3=(f4+f3)f2=f5f2
是不是有些奥妙重重?
那我们就得到结论
Sn=fn+2f2=2fn+fn1f2
这样求和问题就解决了

那如果上来一个负数一个正数怎么办
观察ai的取值范围[-10^5,10^5]
那么这一正一负的最坏情况就是1和-10^5
只要累加10^5次就可以把集合中最大的两个数变成正数了
所以小范围暴力递推就可以了
代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define mo 10000007
#define LL long long
using namespace std;
int n,k;LL tot;
int a[100005];
struct matrix
{
    LL m[3][3];
    void clear(){memset(m,0,sizeof(m));}
};
matrix mul(matrix a,matrix b)
{
    matrix c;
    c.clear();
    for (int i=1;i<=2;i++)
        for (int j=1;j<=2;j++)
            for (int k=1;k<=2;k++)
                c.m[i][j]=(c.m[i][j]+a.m[i][k]*b.m[k][j]%mo)%mo;
    return c;
}
void work(int aa,int bb,int k)
{
    matrix ans,x;
    ans.clear();x.clear();
    ans.m[1][1]=aa;
    ans.m[1][2]=bb;
    x.m[1][1]=x.m[1][2]=x.m[2][1]=1;
    for (;k;k>>=1,x=mul(x,x))
        if (k&1) ans=mul(ans,x);
    tot=((LL)tot+ans.m[1][1]*2+ans.m[1][2]-aa)%mo;
    tot=(tot+mo)%mo;
    cout<<tot;
}
main()
{
    scanf("%d%d",&n,&k);
    for (int i=1;i<=n;i++) scanf("%d",a+i);
    sort(a+1,a+n+1);
    for (int i=1;i<=n-2;i++)
        tot=(tot+a[i])%mo;
    int x=a[n],y=a[n-1];
    for (int i=1;i<=k;i++)
        if(y>=0) {work(x,y,k-i+1);return 0;}
        else tot=(tot+y)%mo,y=x+y;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值