【题目链接】
http://www.lydsy.com/JudgeOnline/problem.php?id=3675
【题意】
将n个数的序列分割k次,每次的利益为分割后两部分数值和的积,求最大利益。
【思路】
设f[i][j]表示将前i个分割j次的最大获益,则有转移式:
f[i][j]=max{ f[k][j-1]+(S(i)-S(k))*S(k) }
设a<b,若b决策优于a决策则有:
(S[b]^2-S[a]^2+f[a][j-1]-f[b][j-1])/(S[b]-S[a])<S[i]
单调队列维护下凸包,每次保持队首的最优性,维护队尾的下凸性。队列中的点至少要有一个。
【代码】
1 #include<set> 2 #include<cmath> 3 #include<queue> 4 #include<vector> 5 #include<cstdio> 6 #include<cstring> 7 #include<iostream> 8 #include<algorithm> 9 #define trav(u,i) for(int i=front[u];i;i=e[i].nxt) 10 #define FOR(a,b,c) for(int a=(b);a<=(c);a++) 11 using namespace std; 12 13 typedef long long ll; 14 const int N = 2e5+10; 15 16 ll read() { 17 char c=getchar(); 18 ll f=1,x=0; 19 while(!isdigit(c)) { 20 if(c=='-') f=-1; c=getchar(); 21 } 22 while(isdigit(c)) 23 x=x*10+c-'0',c=getchar(); 24 return x*f; 25 } 26 27 int n,K,tot; 28 ll f[N][2],cur,q[N],qh,qt,a[N],S[N]; 29 /* 30 double slop(ll a,ll b) { 31 return (double)(S[b]*S[b]-S[a]*S[a]+f[a][cur^1]-f[b][cur^1])/(double)(S[b]-S[a]); 32 } 33 */ 34 ll up(ll a,ll b) 35 { 36 return (S[b]*S[b]-S[a]*S[a]+f[a][cur^1]-f[b][cur^1]); 37 } 38 ll down(ll a,ll b) 39 { 40 return (S[b]-S[a]); 41 } 42 43 int main() 44 { 45 n=read(),K=read(); 46 FOR(i,1,n) { 47 a[++tot]=read(); 48 if(!a[tot]) --tot; 49 } 50 n=tot; 51 FOR(i,1,n) S[i]=S[i-1]+a[i]; 52 FOR(round,1,K) { 53 cur^=1; 54 qh=1; qt=0; 55 FOR(i,round,n) { 56 while(qh<qt&up(q[qh],q[qh+1])<S[i]*down(q[qh],q[qh+1])) qh++; 57 int t=q[qh]; 58 f[i][cur]=f[t][cur^1]+(S[i]-S[t])*S[t]; 59 while(qh<qt&&up(q[qt-1],q[qt])*down(q[qt-1],i)>up(q[qt-1],i)*down(q[qt-1],q[qt])) qt--; 60 q[++qt]=i; 61 } 62 } 63 printf("%lld ",f[n][cur]); 64 return 0; 65 }