BZOJ 3675 [Apio2014]序列分割 (斜率优化DP)

洛谷传送门

题目大意:让你把序列切割k次,每次切割你能获得 这一整块两侧数字和的乘积 的分数,求最大的分数并输出切割方案

神题= =

搞了半天也没有想到切割顺序竟然和答案无关...我太弱了

证明很简单,就是乘法分配律,把式子展开就行了

定义$s_{i}$为序列$a$的前缀和,定义$f[k][i]$表示第$k$次切割是在第$i$个位置的后面,$f[k][i]=max(f[k-1][j]+(s_{i}-s_{j})*(s_{n}-s_{i}))$

展开式子,移项,发现$x$递增,斜率$k$也递增,用队列维护上凸包就行了

至于记录方案,另开一个数组,记录从哪转移来的就行了

复杂度$O(nk)$

又没长记性把$i$打成$j$了(捂脸)

 1 #include <cmath>
 2 #include <queue>
 3 #include <vector>
 4 #include <cstdio>
 5 #include <cstring>
 6 #include <algorithm>
 7 #define N1 101000
 8 #define M1 205
 9 #define ll long long
10 #define dd double  
11 #define uint unsigned int
12 #define idx(X) (X-'0')
13 using namespace std;
14 
15 int gint()
16 {
17     ll ret=0;int fh=1;char c=getchar();
18     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
19     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
20     return ret*fh;
21 }
22 int n,K;
23 int a[N1];
24 ll sa[N1],f[2][N1],x[N1],y[N1];
25 int fa[M1][N1];
26 int que[N1],ret[N1];
27 
28 int main()
29 {
30     freopen("t2.in","r",stdin);
31     scanf("%d%d",&n,&K);
32     for(int i=1;i<=n;i++)
33         a[i]=gint(),sa[i]=sa[i-1]+a[i];
34     int now=1,pst=0;
35     for(int k=1;k<=K;k++)
36     {
37         int hd=1,tl=0,j;
38         que[++tl]=0;
39         for(int i=1;i<n;i++)
40         {
41             while(hd+1<=tl&&(y[que[hd+1]]-y[que[hd]])>=-(x[que[hd+1]]-x[que[hd]])*sa[i])
42                 hd++;
43             j=que[hd];
44             f[now][i]=f[pst][j]+(sa[i]-sa[j])*(sa[n]-sa[i]);
45             fa[k][i]=j;
46             x[i]=sa[i],y[i]=f[pst][i]-sa[i]*sa[n];
47             while(hd+1<=tl&&(y[i]-y[que[tl-1]])*(x[que[tl]]-x[que[tl-1]])>=(y[que[tl]]-y[que[tl-1]])*(x[i]-x[que[tl-1]]))
48                 tl--;
49             que[++tl]=i;
50         }swap(now,pst);
51     }
52     ll ans=0,id=0;
53     for(int i=1;i<n;i++)
54         if(f[pst][i]>ans)
55         ans=f[pst][i],id=i;
56     for(int k=K;k>=1;k--)
57         ret[k]=id,id=fa[k][id];
58     printf("%lld\n",ans);
59     for(int k=1;k<=K;k++)
60         printf("%d ",ret[k]);
61     puts("");
62     return 0;
63 }

 

posted @ 2018-12-01 19:14  guapisolo  阅读(143)  评论(0编辑  收藏  举报