zsyzlzy

导航

 

有点复杂的斜率优化题。

前置知识:

i=1nj=i+1naiaj=(i=1nai)2i=1nai22        (1)\sum_{i=1}^n \sum_{j=i+1}^n a_i*a_j=\dfrac{(\sum_{i=1}^n a_i)^2- \sum_{i=1}^n a_i^2}{2}~~~~~~~~ (1)
证明:
方法1:
ab=(a+b)2a2b22ab=\dfrac{(a+b)^2-a^2-b^2}{2}
ab+ac+bc=(a+b+c)2a2b2c22ab+ac+bc=\dfrac{(a+b+c)^2-a^2-b^2-c^2}{2}
……………………
方法2:
(1)式等价于i=1nj=1naiaj=(i=1ai)(i=1ai)\sum_{i=1}^n \sum_{j=1}^n a_i*a_j =(\sum_{i=1}a_i)*(\sum_{i=1}a_i)
很明显,这是正确的。因为aiaja_i*a_j就相当于从左边括号取一个数乘上右边括号的一个数。

思路:

题目要求的是把n个数分成连续的m+1段的最小费用,每一段的费用等于段内每个数两两相乘的和。
f[i][j]ija[i]isa设f[i][j]表示把前i个数分成j段的最小费用,a[i]为第i个数,s为a的前缀和,ss[i]=j=1ia[i]2ss[i]=\sum_{j=1}^i a[i]^2
则有:f[i][j]=min   f[k][j1]+(s[i]s[k])2(ss[i]ss[k])2f[i][j]=min~~~f[k][j-1]+\dfrac{(s[i]-s[k])^2-(ss[i]-ss[k])}{2}
         f[i][j]=f[k][j1]+2s[i]s[k]+s[i]2ss[i]+s[k]2+ss[k]2~~~~~~~~~f[i][j]=f[k][j-1]+\dfrac{-2s[i]*s[k]+s[i]^2-ss[i]+s[k]^2+ss[k]}{2}
化成            y                      =   k     x   +          b                      ~~~~~~~~~~~~y~~~~~~~~~~~~~~~~~~~~~~=~~~k~~~~~x~~~+~~~~~~~~~~b~~~~~~~~~~~~~~~~~~~~~~的形式
f[k][j1]+ss[k]+s[k]22=s[i]s[k]+f[i][j]s[i]2ss[i]2f[k][j-1]+\dfrac{ss[k]+s[k]^2}{2}=s[i]*s[k]+f[i][j]-\dfrac{s[i]^2-ss[i]}{2}
斜率、横坐标单调递增,就可做了。
代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1010;
int n,m,q[N],l,r,u,v;
ll f[N][2],s[N],ss[N];
ll x(int i){return s[i];}
double y(int i){return f[i][v]+0.5*(s[i]*s[i]+ss[i]);}
bool pd(int i,int j,int k)
{
	return (y(j)-y(i))*(x(k)-x(j))<(y(k)-y(j))*(x(j)-x(i));
}
#define g getchar()
template<class o>
void qr(o&x)
{
	char c=g;x=0;
	while(!('0'<=c&&c<='9'))c=g;
	while('0'<=c&&c<='9')x=x*10+c-'0',c=g;
}
int main()
{
	while(1)
	{
		qr(n);qr(m);
		if(!n&&!m)return 0;m++;
		for(int i=1;i<=n;i++)
			qr(s[i]),ss[i]=s[i]*s[i]+ss[i-1],s[i]=s[i]+s[i-1];
		for(int i=1;i<=n;i++)
			f[i][0]=(s[i]*s[i]-ss[i])>>1;
		u=1;v=0;
		for(int j=2;j<=m;j++,swap(u,v))
		{
			l=r=1;q[l]=j-1;
			for(int i=j,k;i<=n;i++)
			{
				while(l<r&&y(q[l+1])-y(q[l])<=s[i]*(x(q[l+1])-x(q[l])))l++;
				k=q[l];
				f[i][u]=f[k][v]+((1LL*(s[i]-s[k])*(s[i]-s[k])-ss[i]+ss[k])>>1);
				while(l<r&&!pd(q[r-1],q[r],i))r--;
				q[++r]=i;
			}
		}
		printf("%lld\n",f[n][v]);
	}
}
posted on 2019-07-03 10:18  zsyzlzy  阅读(99)  评论(0编辑  收藏  举报