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