P4072 [SDOI2016]征途 斜率优化

题意:

戳这里

分析:

  • 暴力

$ O(n^3) $ 枚举转移,期望得分 \(60pts\)

  • 正解

化简式子,由于 m 和 n 是定值,所以平均数也可以看做定值

\(\large s^2 = \frac{\sum(a_i-\frac{sum}{m})^2}{m}=\frac{\sum {a_i}^2-2\times\frac{sum}{m}\sum a_i+\sum {\frac{sum}{m}}^2}{m}\)

\(\large s^2 =\frac{\sum {a_i}^2-2\times{\frac{sum}{m}}\times sum+m\times \frac{sum^2}{m^2}}{m}\)

\(\large s^2 = \frac{\sum {a_i}^2}{m}-\frac{sum^2}{m^2}\)

\(m^2\) 带入

\(ans= m\times s^2 = m\times\sum {a_i}^2-sum^2\)

问题转化成了求 \(\sum {a_i}^2\) 的最小值,我们发现这个形式很 斜率优化

\(j>k\)\(j\) 转移比 \(k\) 更优,那么有

\(f_j+(sum_i-sum_j)^2<f_k+(sum_i-sum_k)^2\)

\(f_j+{sum_j}^2-2\times sum_i\times sum_j<f_k+{sum_k}^2-2\times sum_i\times sum_k\)

\(\large \frac{(f_j+{sum_j}^2)+({sum_k}^2+f_k)}{sum_j-sum_k}< 2\times sum_i\)

维护 \(f_j+{sum_j}^2\) 就可以了

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
	int read()
	{
		int x=0,f=1;char ch=getchar();
		while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
		while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	} 
	
	const int maxn = 3005;
	int n,m,h,t;
	int f[maxn][maxn],s1[maxn],s2[maxn],a[maxn],q[maxn];
	
	int sqr(int x){return x*x;}
	int upn(int id,int j,int k){return f[id][j]+s2[j]-f[id][k]-s2[k];}
	int dwn(int j,int k){return s1[j]-s1[k];}
	
	void work()
	{
		memset(f,0x3f,sizeof(f));
		n=read();m=read();
		for(int i=1;i<=n;i++) a[i]=read(),s1[i]=s1[i-1]+a[i],s2[i]=sqr(s1[i]);
		for(int i=1;i<=n;i++) f[1][i]=s2[i];
		for(int j=2;j<=m;j++)
		{
			h=1,t=0;
			for(int i=1;i<=n;i++)
			{
				while(h<t&&upn(j-1,q[h+1],q[h])<2*s1[i]*dwn(q[h+1],q[h])) h++;
				f[j][i]=f[j-1][q[h]]+sqr(s1[i]-s1[q[h]]);
				while(h<t&&upn(j-1,q[t],q[t-1])*dwn(i,q[t])>upn(j-1,i,q[t])*dwn(q[t],q[t-1])) t--;
				q[++t]=i;
			}
		}
		printf("%lld\n",1ll*m*f[m][n]-sqr(s1[n]));
	} 
	
}

int main()
{
	zzc::work();
	return 0;
}
posted @ 2020-12-08 22:22  youth518  阅读(72)  评论(0编辑  收藏  举报