Hdu5171 小奇的集合

题意

有一个大小为n的可重集S,小奇每次操作可以加入一个数a+b(a,b均属于S),求k次操作后它可获得的S的和的最大值。(数据保证这个值为非负数)

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

GXZlegend的题解

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

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

具体过程:

\[\left[ \begin{matrix} a & b &s \end{matrix} \right] * \left[ \begin{matrix} 1 & 1& 1\\ 1 & 0& 0\\ 0 & 0& 1 \end{matrix} \right]= \left[ \begin{matrix} a+b & a &s+a \end{matrix} \right] \]

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

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

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

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
	rg T data=0,w=1;
	rg char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') w=-1;
		ch=getchar();
	}
	while(isdigit(ch))
		data=data*10+ch-'0',ch=getchar();
	return data*w;
}
template<class T>il T read(rg T&x){
	return x=read<T>();
}
typedef long long ll;

co int mod=1e7+7;
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];};
	co ll*operator[](int a)co{return v[a];};
	data operator*(co data&a)co{
		data re;
		for(int k=0;k<3;++k)
			for(int i=0;i<3;++i) if(v[i][k])
				for(int j=0;j<3;++j)
					re[i][j]=(re[i][j]+v[i][k]*a[k][j])%mod;
		return re;
	}
}A;
int a[100001];
data pow(data x,int y){
	data re(1);
	while(y){
		if(y&1) re=re*x;
		x=x*x,y>>=1;
	}
	return re;
}
int main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	int n=read<int>(),k=read<int>();
	ll sum=0;
	for(int i=1;i<=n;++i)
		sum+=read(a[i]);
	std::sort(a+1,a+n+1);
	if(a[n]<=0) printf("%lld\n",((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\n",((sum+a[n]*A[0][2]+a[n-1]*A[1][2])%mod+mod)%mod);
	}
	return 0;
}

posted on 2019-02-21 07:38  autoint  阅读(246)  评论(0)    收藏  举报

导航