题目描述
题解
由于数据保证最后的答案为非负数,那么序列就有两种情况:
1、序列中有两个或两个以上非负数。
2、序列中只有一个非负数。
对于第一种情况,每一次的操作一定是将最大的两个数相加放入序列中。比如有不降序列:
a1,a2,a3……,an−1,an
,进行多次操作就可以写出如下的序列:
a1,a2,a3…an−1,an,an−1+an
a1,a2,a3…an−1,an,an−1+an,an−1+2an
a1,a2,a3…an−1,an,an−1+an,an−1+2an,2an−1+3an
a1,a2,a3…an−1,an,an−1+an,an−1+2an,2an−1+3an,3an−1+5an
...
可以看出
an−1
和
an
的系数呈斐波那契数列。那么我们就需要求斐波那契数列的前缀和,这个
O(k)
的递推可以用矩阵快速幂来加速。
对于第二种情况,一定是唯一的一个非负数数和数列中的最大的负数相加,直到数列中有两个非负数,再进行第一种操作。
我的方法比较麻烦,用等差数列的求和公式求出负数和非负数相加过程中的和。实际上由于
|ai|<=100000
,可以直接暴力计算。
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long
#define N 100005
#define Mod 10000007
int n,p;
struct hp{LL a[5][5];}A,unit,mi,ans;
LL a[N],sum,s1,s2,k1,k2;
inline hp cheng(hp a,hp b)
{
hp ans;
memset(ans.a,0,sizeof(ans.a));
for (int i=1;i<=3;++i)
for (int j=1;j<=3;++j)
for (int k=1;k<=3;++k)
ans.a[i][j]=(ans.a[i][j]+a.a[i][k]*b.a[k][j]%Mod)%Mod;
return ans;
}
inline hp fast_pow(hp a,int p)
{
hp ans=unit;
for (;p;p>>=1,a=cheng(a,a))
if (p&1)
ans=cheng(ans,a);
return ans;
}
int main()
{
scanf("%d%d",&n,&p);
for (int i=1;i<=n;++i) scanf("%lld",&a[i]),sum=(sum+a[i])%Mod;
sort(a+1,a+n+1);
if (a[n-1]<0)
{
int t;
if ((-a[n-1])%a[n]==0) t=(int)(-a[n-1])/a[n];
else t=(int)((-a[n-1])/a[n])+1;
p-=t;
a[n+1]=t*a[n]+a[n-1];
s1=min(a[n],a[n+1]),s2=max(a[n],a[n+1]);
LL nn=(t-1)*t/2;
nn%=Mod;
sum=(sum+(t*a[n+1]%Mod-a[n]*nn%Mod)%Mod)%Mod;
}
else
{
s1=a[n-1]; s2=a[n];
}
if (p==0)
{
printf("%lld\n",sum);
return 0;
}
for (int i=1;i<=3;++i)
for (int j=1;j<=3;++j) unit.a[i][j]=ans.a[i][j]=0;
for (int i=1;i<=3;++i) unit.a[i][i]=1;
A.a[1][1]=A.a[2][1]=A.a[2][2]=A.a[2][3]=A.a[3][2]=1;
mi=fast_pow(A,p-1);
ans.a[1][1]=ans.a[1][2]=ans.a[1][3]=1;
ans=cheng(ans,mi);
k1=ans.a[1][1]; k2=(ans.a[1][1]-1+ans.a[1][2])%Mod;
sum=(sum+(s1*k1%Mod+s2*k2%Mod)%Mod)%Mod;
sum=((sum%Mod)+Mod)%Mod;
printf("%lld\n",sum);
}
总结
1、考虑问题情况要全面。

本文针对一种特定算法竞赛题目提供了解决方案,通过分析序列中非负数的操作规律,利用斐波那契数列和矩阵快速幂的方法高效求解。文章详细介绍了如何根据不同情况采取最优策略,并给出了完整的代码实现。
761

被折叠的 条评论
为什么被折叠?



