1. 程式人生 > >2018.09.29 bzoj3675: [Apio2014]序列分割(斜率優化dp)

2018.09.29 bzoj3675: [Apio2014]序列分割(斜率優化dp)

傳送門 斜率優化dp經典題目。 首先需要證明只要選擇的K個斷點是相同的,那麼得到的答案也是相同的。 根據分治的思想,我們只需要證明有兩個斷點時成立,就能推出K個斷點時成立。 我們設兩個斷點分成的三段連續序列的和為a,b,ca,b,c 如果先分左邊有:total=a(b+c)+bc=ab+bc+catotal=a*(b+c)+b*c=a*b+b*c+c*a 如果先分右邊有:total=(a+b)c+ab=ab+bc+catotal=(a+b)*c+a*b=a*b+b*c+c*a

b=ab+bc+ca 是相同的。 那麼這樣我們就可以按從左到右劃分斷點的思路來進行dp了。 令f[i][j]f[i][j]為前j個數i個斷點能夠劃分出的最大值。 那麼顯然有: f[i]=maxf[i]=max{f[j]+sum[j](sum[i]sum[j])f[j]+sum[j]*(sum[i]-sum[j])} f[i]=maxf[i]=max{f[j]sum[j]2f[j]-sum[j]^2}+sum[i]sum[j]+sum[i]*sum[j] if(k1<k2if(k1<k2&&calc(k1)calc(k2))calc(k1)\le calc(k2))隊頭出隊 =>f[k1]sum[k1]2+sum[i]sum[k1]f[k2]sum[k2]2+sum[i]sum[k2]f[k1]-sum[k1]^2+sum[i]*sum[k1]\le f[k2]-sum[k2]^2+sum[i]*sum[k2] =>f[k1]sum[k1]2f[k2]+sum[k2]2(sum[k2]sum[k1])sum[i]f[k1]-sum[k1]^2-f[k2]+sum[k2]^2\le (sum[k2]-sum[k1])*sum[i] =>((f[k1]sum[k1]2)(f[k2]sum[k2]2))/(sum[k2]sum[k1]sum[i]((f[k1]-sum[k1]^2)-(f[k2]-sum[k2]^2))/(sum[k2]-sum[k1]\le sum[i] 隊尾出隊:slope(q[tl1],q[tl])>=slope(q[tl],i)slope(q[tl-1],q[tl])>=slope(q[tl],i) 程式碼:

#include<bits/stdc++.h>
#define N 100005
#define ll long long
using namespace std;
inline ll read(){
	ll ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int n,K,hd,tl,q[N],tmp;
ll f[2][N],sum[N];
inline ll calcX(int i,int j){return sum[j]-sum[i];}
inline ll calcY(int i,int j){return (f[tmp^1][i]-sum[i]*sum[i])-(f[tmp^1][j]-sum[j]*sum[j]);}
int main(){
	n=read(),K=read(),tmp=0;
	for(int i=1;i<=n;++i)sum[i]=sum[i-1]+read();
	for(int i=1;i<=K;++i){
		hd=tl=1;
		for(int j=1;j<=n;++j){
			while(hd<tl&&calcY(q[hd],q[hd+1])<=calcX(q[hd],q[hd+1])*sum[j])++hd;
			int k=q[hd];
			f[tmp][j]=f[tmp^1][k]+sum[k]*(sum[j]-sum[k]);
			while(hd<tl&&calcY(q[tl-1],q[tl])*calcX(q[tl],j)>=calcY(q[tl],j)*calcX(q[tl-1],q[tl]))--tl;
			q[++tl]=j;
		}
		tmp^=1;
	}
	printf("%lld",f[tmp^1][n]);
	return 0;
}