bzoj 4547: Hdu5171 小奇的集合 (矩阵乘法+递推)

4547: Hdu5171 小奇的集合

Time Limit: 2 Sec   Memory Limit: 256 MB
Submit: 166   Solved: 77
[ Submit][ Status][ Discuss]

Description

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

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

Input

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

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

Output

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

Sample Input

2 2
3 6

Sample Output

33

HINT

Source

By Hzwer

[ Submit][ Status][ Discuss]


题解:矩阵乘法+递推

发现每次都是从集合中选取最大的两个数把他们的和加入集合中, 那么以样例为例

3,6, 3+6,3+6+6,3+3+6+6+6.......可以发现最大值,和次大值的在每项中的系数满足斐波那契数列.

于是就牵扯到了斐波那契数列求前缀和.

奇数项求和bzoj 4547: Hdu5171 小奇的集合 (矩阵乘法+递推)_第1张图片

偶数项求和

bzoj 4547: Hdu5171 小奇的集合 (矩阵乘法+递推)_第2张图片
斐波那契的某一项可以用矩阵快速求得.

貌似直接构造矩阵求前缀和亦可.

#include
#include
#include
#include
#include
#define p 10000007
#define N 100005
#define LL long long
using namespace std;
int n,m,k,m1,m2;
LL a[N*2],ans;
struct data 
{
	LL x[10][10];
}num,e,t,t1;
void clear(data &a)
{
	for (int i=1;i<=k;i++)
	 for (int j=1;j<=k;j++)
	  a.x[i][j]=0;
}
void change(data &a,data b)
{
	for (int i=1;i<=k;i++)
	 for (int j=1;j<=k;j++)
	  a.x[i][j]=b.x[i][j];
}
data mul(data a,data b)
{
	data c; 
	for(int i=1;i<=k;i++)
	 for (int j=1;j<=k;j++)
	 {
	 	c.x[i][j]=0;
	 	for (int t=1;t<=k;t++)
	 	 c.x[i][j]=(c.x[i][j]+a.x[i][t]*b.x[t][j]%p)%p;
	 }
	return c;
}
data pow(data num,int x)
{
	data ans; clear(ans);
	for (int i=1;i<=k;i++)  ans.x[i][i]=1;
	data base;
	change(base,num);
	while(x)
	{
		if (x&1)  ans=mul(ans,base);
		x>>=1;
		base=mul(base,base);
	}
	return ans;
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	 scanf("%lld",&a[i]),ans=(ans+a[i])%p;
	sort(a+1,a+n+1);
	while (a[n-1]<0)
	 {
	 	a[n-1]+=a[n]; ans=(ans+a[n-1])%p;
	 	m--;
	 }
	if (a[n-1]>a[n])  swap(a[n-1],a[n]);
	k=2;
	clear(num);
	num.x[1][1]=1; num.x[1][2]=1;
	num.x[2][1]=1; num.x[2][2]=0;
  	if (m%2)  {
	    m1=m+1;
		m2=m;
	}
	else
	{
		m1=m;
		m2=m+1;
	}
	data b=pow(num,m1);
    data c=pow(num,m2);
    ans=(ans+a[n-1]%p*((b.x[1][2]+c.x[1][2]-1)%p))%p;
    m++;
    if (m%2)  {
	    m1=m+1;
		m2=m;
	}
	else
	{
		m1=m;
		m2=m+1;
	}
	b=pow(num,m1);
    c=pow(num,m2);
    ans=(ans+a[n]%p*((b.x[1][2]+c.x[1][2]-2)%p))%p;
    printf("%lld\n",(ans+p)%p);
}


你可能感兴趣的:(矩阵)