bzoj4547 Hdu5171 小奇的集合 矩阵乘法

版权声明:虽然是个蒟蒻但是转载还是要说一声的哟 https://blog.csdn.net/jpwang8/article/details/83028800

Description


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

对于100%的数据,有 n<=105,k<=109,|ai|<=105
输出一个整数,表示和的最大值。答案对10000007取模。

Solution


一开始没看到k的范围。。。sb了
然后看到答案保证非负以为只有正正和正负的情况。。。又sb了

这个a的绝对值引人深撕。显然每次贪心地取两个最大值一定是最优秀的。
考虑初始两个最大值都是正整数的情况,感受一下可以发现贡献就是斐波那契数列求和
考虑初始两个最大值一正一负的情况,我们可以暴力把负数加成非负数,然后同上
考虑初始两个最大值都是负数,显然就是这两个数取k次
斐波那契数列求和的话s[n]=f[n+2]-1,矩阵上即可

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

typedef long long LL;
const int MOD=10000007;
const int N=200005;

int a[N];

struct Mat {
	LL r[3][3];

	Mat() {fill(r,0);}

	Mat operator *(Mat B) const {
		Mat A=*this,C;
		rep(i,1,2) rep(j,1,2) {
			rep(k,1,2) C.r[i][j]=(C.r[i][j]+A.r[i][k]*B.r[k][j]%MOD)%MOD;
		}
		return C;
	}

	Mat operator ^(int x) const {
		Mat A=*this,C=A; x--;
		for (;x;x>>=1) {
			if (x&1) C=C*A;
			A=A*A;
		}
		return C;
	}
} A;

bool cmp(int x,int y) {
	return x>y;
}

int main(void) {
    freopen("data.in","r",stdin);
    freopen("myp.out","w",stdout);
	int n,k,ans=0; scanf("%d%d",&n,&k);
	rep(i,1,n) {
		scanf("%d",&a[i]);
		ans=(ans+a[i])%MOD;
	}
	std:: sort(a+1,a+n+1,cmp);
	if (a[1]<0&&a[2]<0) {
		ans=(ans+(a[1]+a[2])*k%MOD)%MOD;
		ans=(ans%MOD+MOD)%MOD;
		printf("%d\n", ans);
		return 0;
	}
	if (a[1]>0&&a[2]<0) {
		while (k) {
			k--;
			a[++n]=a[2];
			a[2]+=a[1]; ans=(ans+a[2])%MOD;
			if (a[2]>=0) break;
		}
		std:: sort(a+1,a+n+1,cmp);
	}
	A.r[2][1]=A.r[1][2]=A.r[2][2]=1;
	A=A^(k+2);
	ans=(ans+(A.r[2][1]-1)*a[2]%MOD+(A.r[2][2]-2)*a[1]%MOD)%MOD;
	ans=(ans%MOD+MOD)%MOD;
	printf("%d\n", ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/83028800