zoukankan      html  css  js  c++  java
  • 【bzoj4547】Hdu5171 小奇的集合 矩阵乘法

    题目描述

     有一个大小为n的可重集S,小奇每次操作可以(此处“可以”指的是“必须”)加入一个数a+b(a,b均属于S),求k次操作后它可获得的S的和的最大

    值。(数据保证这个值为非负数)

    输入

    第一行有两个整数n,k表示初始元素数量和操作数,第二行包含n个整数表示初始时可重集的元素。

    对于100%的数据,有 n<=10^5,k<=10^9,|ai|<=10^5

    输出

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

    样例输入

    2 2
    3 6

    样例输出

    33


    题解

    矩阵乘法

    显然每次选择集合中最大的两个数相加即可。

    如果最大的两个数都是正数,那么结果显然也是正数并且比它们都要大,即$(b,a) o(a,a+b)$,所以可以使用矩阵乘法来解决。

    具体过程:$egin{bmatrix}a&b&send{bmatrix}*egin{bmatrix}1&1&1\1&0&0\0&0&1end{bmatrix}=egin{bmatrix}a+b&a&s+aend{bmatrix}$

    如果最大的两个数都是负数,那么结果显然也是负数并且比它们都要小,因此直接选择它们相加k次即可。

    如果这两个数一正一负,那么每次负数会变大,由于数的范围只有$10^5$,因此可以暴力操作直到加到正数或者没有操作机会,然后矩乘即可。

    时间复杂度$O(a+log k)$

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define mod 10000007
    using namespace std;
    typedef long long ll;
    struct data
    {
        ll v[3][3];
        data(int x = 0) {memset(v , 0 , sizeof(v)) , v[0][0] = v[1][1] = v[2][2] = x;}
        ll *operator[](int a) {return v[a];}
        data operator*(data a)
        {
            data ans;
            int i , j , k;
            for(i = 0 ; i < 3 ; i ++ )
                for(j = 0 ; j < 3 ; j ++ )
                    for(k = 0 ; k < 3 ; k ++ )
                        ans[i][j] = (ans[i][j] + v[i][k] * a[k][j]) % mod;
            return ans;
        }
    }A;
    int a[100010];
    data pow(data x , int y)
    {
        data ans(1);
        while(y)
        {
            if(y & 1) ans = ans * x;
            x = x * x , y >>= 1;
        }
        return ans;
    }
    int main()
    {
        int n , k , i;
        ll sum = 0;
        scanf("%d%d" , &n , &k);
        for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a[i]) , sum += a[i];
        sort(a + 1 , a + n + 1);
        if(a[n] <= 0) printf("%lld
    " , ((sum + (ll)(a[n] + a[n - 1]) * k) % mod + mod) % mod);
        else
        {
            for(sum -= a[n] ; k && a[n - 1] < 0 ; k -- )
                a[n - 1] += a[n] , sum += a[n - 1];
            A[0][0] = A[0][1] = A[0][2] = A[1][0] = A[2][2] = 1 , A = pow(A , k + 1);
            printf("%lld
    " , ((sum + a[n] * A[0][2] + a[n - 1] * A[1][2]) % mod + mod) % mod);
        }
        return 0;
    }
    
  • 相关阅读:
    C语言的存储类别和动态内存分配
    C语言中复杂的声明
    C语言中typedef的解释_2
    C语言中类型限定符
    C语言文件I/O和标准I/O函数
    C语言中存储类别、链接与内存管理
    C++中static与const成员
    C++多态、虚函数、纯虚函数、抽象类
    sizeof结构体
    杂类
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7692162.html
Copyright © 2011-2022 走看看