[APIO2014]序列分割

题面描述

传送门

思路

对于同一种分割方式,顺序不会影响最终结果。

分析样例 ( 4 , 1 , 3 , 4 , 0 , 2 , 3 ) (4,1,3,4,0,2,3) (4,1,3,4,0,2,3)

如果先切 5 5 5,则 ( 4 + 1 + 3 + 4 + 0 ) ∗ ( 2 + 3 ) = 60 (4+1+3+4+0)*(2+3)=60 (4+1+3+4+0)(2+3)=60

再切 3 3 3,则 ( 4 + 1 + 3 ) ∗ ( 4 + 0 ) = 32 (4+1+3)*(4+0)=32 (4+1+3)(4+0)=32

最后切 1 1 1,则 4 ∗ ( 1 + 3 ) = 16 4*(1+3)=16 4(1+3)=16

结果 60 + 32 + 16 = 108 60+32+16=108 60+32+16=108

通过玄学研究观察,我们发现最后分成块的元素:

  1. 一定不会与同块的元素相乘对答案产生贡献
    (因为只有分块时,才会进行操作,对答案产生贡献)
  2. 会与其他块的所有元素进行相乘且仅乘一次,对答案产生贡献

那么我们就可以判断:

对于同一种分割方式,顺序不会影响最终结果.

因此仅有切的位置会影响结果

所以这是我们可以推出状态转移方程

s s s a a a的前缀和

F i , p + 1 = max ⁡ ( F j , p + s j ∗ ( s i − s j ) ) F_{i,p+1}=\max(F_{j,p}+s_j*(s_i-s_j)) Fi,p+1=max(Fj,p+sj(sisj))

决策单调性

F k , p + s k ∗ ( s i − s k ) ≥ F j , p + s j ∗ ( s i − s j ) ( j &lt; k &lt; i ) F_{k,p}+s_k*(s_i-s_k)\ge F_{j,p}+s_j*(s_i-s_j)(j&lt;k&lt;i) Fk,p+sk(sisk)Fj,p+sj(sisj)(j<k<i)

正好符合

  1. 一定不会与同块的元素相乘对答案产生贡献
    (因为只有分块时,才会进行操作,对答案产生贡献)
  2. 会与其他块的所有元素进行相乘且仅乘一次,对答案产生贡献

在未来状态 t t t下,

证明:

F k , p + s k ∗ ( s t − s k ) ≥ F j , p + s j ∗ ( s t − s j ) F_{k,p}+s_k*(s_t-s_k)\ge F_{j,p}+s_j*(s_t-s_j) Fk,p+sk(stsk)Fj,p+sj(stsj)

由于 s t = s i + v a l s_t=s_i+val st=si+val

F k , p + s k ∗ ( s i + v a l − s k ) ≥ F j , p + s j ∗ ( s i + v a l − s j ) F_{k,p}+s_k*(s_i+val-s_k)\ge F_{j,p}+s_j*(s_i+val-s_j) Fk,p+sk(si+valsk)Fj,p+sj(si+valsj)

只需证明:

s k ∗ v a l ≥ s j ∗ v a l s_k*val\ge s_j*val skvalsjval

由于 v a l &gt; 0 , s k ≥ s j val&gt;0,s_k\ge s_j val>0,sksj

证毕。

注意,为了保证单调性,我们要避免 s k = = s j s_k== s_j sk==sj,避免出锅

踢队头

F k , p + s k ∗ ( s i − s k ) ≥ F j , p + s j ∗ ( s i − s j ) ( j &lt; k &lt; i ) F_{k,p}+s_k*(s_i-s_k)\ge F_{j,p}+s_j*(s_i-s_j)(j&lt;k&lt;i) Fk,p+sk(sisk)Fj,p+sj(sisj)(j<k<i)

F k , p + s k ∗ ( s i − s k ) ≥ F j , p + s j ∗ ( s i − s j ) F_{k,p}+s_k*(s_i-s_k)\ge F_{j,p}+s_j*(s_i-s_j) Fk,p+sk(sisk)Fj,p+sj(sisj)

F j , p − F k , p − s j 2 + s k 2 ≤ s i ∗ ( s k − s j ) F_{j,p}-F_{k,p}-{s_j}^2+{s_k}^2\le s_i*(s_k-s_j) Fj,pFk,psj2+sk2si(sksj)

由于 s k − s j &gt; 0 s_k-s_j&gt;0 sksj>0

c a l c ( j , k ) = F j , p − F k , p − s j 2 + s k 2 s k − s j ≤ s i calc(j,k)=\frac{F_{j,p}-F_{k,p}-{s_j}^2+{s_k}^2}{s_k-s_j}\le s_i calc(j,k)=sksjFj,pFk,psj2+sk2si

c a l c ( j , k ) ≤ s i calc(j,k)\le s_i calc(j,k)si时, k k k优于 j j j.

因此当 c a l c ( q h e a d , q h e a d + 1 ) ≤ s i calc(q_{head},q_{head+1})\le s_i calc(qhead,qhead+1)si

h e a d + 1 head+1 head+1优于 h e a d head head

由于 s i s_i si i i i增大而增大,那么 c a l c ( q h e a d , q h e a d + 1 ) calc(q_{head},q_{head+1}) calc(qhead,qhead+1) h e a d head head增大而增大,才符合 h e a d head head为最优解,因此斜率是不断递增的,要维护一个下凸壳。

踢队尾

根据斜率是不断递增的,仅当

c a l c ( q t a i l , i ) ≥ c a l c ( q t a i l − 1 , q t a i l ) calc(q_{tail},i)\ge calc(q_{tail-1},q_{tail}) calc(qtail,i)calc(qtail1,qtail)

斜率才满足不断递增。

故当

c a l c ( q t a i l , i ) ≤ c a l c ( q t a i l − 1 , q t a i l ) calc(q_{tail},i)\le calc(q_{tail-1},q_{tail}) calc(qtail,i)calc(qtail1,qtail)

删去队尾。

AC code

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<cstring>
#define ll long long 
#define gc getchar()
using namespace std;
const int N=1e5+10;
inline void qr(ll &x)
{
	x=0;int f=1;char c=gc;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc;}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;}
	x*=f;
}
inline void qw(ll x)
{
	if(x<0)x=-x,putchar('-');
	if(x/10)qw(x/10);
	putchar(x%10+48);
}
ll f[N],g[N],s[N];
int q[N],l,r,pre[N][210];
inline double calc(int j,int k)
{
	if(s[k]==s[j]) return -1e18;
	return (double)(g[j]-g[k]-s[j]*s[j]+s[k]*s[k])/(s[k]-s[j]);
}
int main()
{
	int n,k;scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	{	
		qr(s[i]);s[i]+=s[i-1];
	}
	int tot=0;
	for(int i=1;i<=k;i++)
	{
		l=1;r=1;q[1]=0;memcpy(g,f,sizeof(g));
		for(int j=1;j<=n;j++)
		{
			while(l<r&&calc(q[l],q[l+1])<=(double)s[j])++l;
			f[j]=g[q[l]]+s[q[l]]*(s[j]-s[q[l]]);
			pre[j][i]=q[l];
			while(l<r&&calc(j,q[r])<=calc(q[r-1],q[r]))--r;
			q[++r]=j;
		}
	}
	qw(f[n]);puts("");
	int j=n,i=k;
	while(pre[j][i])printf("%d ",pre[j][i]),j=pre[j][i],--i;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值