1. 程式人生 > >bzoj4547 Hdu5171 小奇的集合 矩陣乘法

bzoj4547 Hdu5171 小奇的集合 矩陣乘法

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; }