zoukankan      html  css  js  c++  java
  • 【BZOJ4547】小奇的集合-矩阵快速幂+等比二分

    测试地址:小奇的集合
    做法:本题需要用到矩阵快速幂+等比二分。
    首先,我们肯定是每次选择集合中两个最大的元素加起来,这样能使最后集合的和最大。
    那么现在有两种情况:
    1.这两个元素加起来比原最大值小,但比原次大值大,这时候我们把次大值替换为它们的和。
    2.这两个元素加起来比原最大值大,这时候次大值就是原最大值,最大值就是它们的和。
    为什么不会出现两个元素加起来比原次大值还小的情况?因为题目保证最后结果不是负数,而出现这种情况的条件是最大值和次大值都是负数,这显然是不可能出现的。
    对于第二种情况,我们可以构造出转移矩阵A,然后要求的就是A+A2+A3+...+Ak与矩阵[maxsecond_max]T的乘积中的第一项,求前面那一串东西可以用等比二分的思想加速,这个思想是这样的:
    令要求的式子为f(k),则:
    k为偶数,则f(k)=f(k/2)+Ak/2f(k/2)
    k为奇数,则f(k)=f(k1)+Ak
    显然这个算法搭配矩阵快速幂是O(log2k)的,那么就可以解决第二种情况。
    而对于第一种情况,我们知道这种情况只会重复不超过权值次,当次大值变为非负数的时候,就变为了第二种情况,所以我们直接模拟这一段过程,再用上面的方法做剩下的部分即可。
    以下是本人代码:

    #include <bits/stdc++.h>
    typedef long long ll;
    const ll mod=10000007;
    int n;
    ll k,mx1=-1000000000,mx2=-1000000000,sum=0;
    struct matrix
    {
        ll mat[2][2];
    }M[35],Ans,E,Zero;
    
    matrix operator * (matrix A,matrix B)
    {
        matrix S;
        memset(S.mat,0,sizeof(S.mat));
        for(int i=0;i<=1;i++)
            for(int j=0;j<=1;j++)
                for(int k=0;k<=1;k++)
                    S.mat[i][j]=(S.mat[i][j]+A.mat[i][k]*B.mat[k][j])%mod;
        return S;
    }
    
    matrix operator + (matrix A,matrix B)
    {
        matrix S;
        for(int i=0;i<=1;i++)
            for(int j=0;j<=1;j++)
                S.mat[i][j]=(A.mat[i][j]+B.mat[i][j])%mod;
        return S;
    }
    
    matrix power(ll x)
    {
        matrix S=E;
        int i=0;
        while(x)
        {
            if (x&1) S=S*M[i];
            x>>=1,i++;
        }
        return S;
    }
    
    matrix calc(ll k)
    {
        if (!k) return Zero;
        if (k%2==0)
        {
            matrix S=calc(k>>1);
            return S+(power(k>>1)*S);
        }
        else return calc(k-1)+power(k);
    }
    
    int main()
    {
        scanf("%d%lld",&n,&k);
        for(int i=1;i<=n;i++)
        {
            ll x;
            scanf("%lld",&x);
            if (x>mx1)
            {
                mx2=mx1;
                mx1=x;
            }
            else if (x>mx2) mx2=x;
            sum=((sum+x)%mod+mod)%mod;
        }
    
        memset(Zero.mat,0,sizeof(Zero.mat));
        E.mat[0][0]=E.mat[1][1]=1;
        E.mat[0][1]=E.mat[1][0]=0;
        M[0].mat[0][0]=M[0].mat[0][1]=M[0].mat[1][0]=1;
        M[0].mat[1][1]=0;
        for(int i=1;i<=32;i++) M[i]=M[i-1]*M[i-1];
    
        while(mx2<0&&k)
        {
            mx2=(mx1+mx2)%mod;
            sum=(sum+mx2)%mod;
            k--;
        }
        Ans=calc(k);
        printf("%lld",(sum+Ans.mat[0][0]*mx1%mod+Ans.mat[0][1]*mx2%mod)%mod);
    
        return 0;
    }
  • 相关阅读:
    Ubuntu18.04安装NAVIDIA驱动
    ubuntu 设置root用户密码并实现root用户登录
    配置ubuntu允许远程SSH连接
    Centos7安装yum命令
    NVDIA往期在线研讨会地址 论坛提问地址
    二进制安装单master节点测试环境k8s集群
    kubeadm初始化k8s-延长证书过期时间
    kubeadm初始化k8s-删除控制节点-重新把控制节点加入集群步骤
    kubeadm安装的多master节点的k8s高可用集群
    二进制安装多master节点的k8s集群
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793474.html
Copyright © 2011-2022 走看看