[Luogu P4983] 忘情

\(\text{Problem}:\)题目链接

感性证明一下凸性。

\(S1=(x+y+z+1)^{2}=x^2+y^2+z^2+2(x+y+z)+2xy+2xz+2yz+1\)

\(S2=(x+y+1)^2+(z+1)^2=x^2+y^2+z^2+2(x+y+z)+2xy+2\)

\(S3=(x+1)^2+(y+1)^2+(z+1)^2=x^2+y^2+z^2+2(x+y+z)+3\)

\(D1=S1-S2=2z(x+y)-1>0\)

\(D2=S2-S3=2xy-1>0\)

\(D1-D2=2x(z-y)+2zy\)

现在要让 \(S2\) 最小,即 \(x,y\leq z\),即 \(D1-D2>0\),那么这是一个左下凸包。

\(wqs\) 二分,求最小值。

即每一段有个额外代价是 \(G\) 的情况,设 \(f_{i}\) 表示前 \(i\) 个的最小值。

\(f_{i}=\min\{f_{j}+(a_{i}-a_{j}+1)^{2}+G\},1\leq j<i\)

拆开转移形式:\(f_{j}+a_{i}^2+a_{j}^2+1-2a_{i}a_{j}+2(a_{i}-a_{j})+G\)

将与 \(j\) 无关的扔出去,\(f_{i}=\min\{f_{j}+a_{j}^2-2a_{i}a_{j}-2a_{j}\}+(a_{i}+1)^2+G\)

发现只有一项和 \(a_{i},a_{j}\) 同时相关,考虑斜率优化。

\(f_{j}+a_{j}^2-2a_{i}a_{j}-2a_{j}\leq f_{k}+a_{k}^2-2a_{i}a_{k}-2a_{k}\)

\(2a_{i}\geq \cfrac{f_{j}+a_{j}^2-2a_{j}-(f_{k}+a_{k}^2-2a_{k})}{a_{j}-a_{k}}\)

直接斜率优化一下就没了。时间复杂度 \(O(n\log n)\)

注意有点卡精度。

\(\text{Code}:\)

#include <bits/stdc++.h>
#pragma GCC optimize(3)
#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
#define double long double
using namespace std; const int N=100010;
inline int read()
{
	int s=0, w=1; ri char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
	return s*w;
}
int n,m,a[N],Ans,F[N],G[N],sta[N],hd,tl;
inline double Slope(int i,int j)
{
	return 1.0*(F[i]+a[i]*a[i]-a[i]*2-F[j]-a[j]*a[j]+a[j]*2)/(1.0*(a[i]-a[j]));
}
inline int Check(int w)
{
	hd=tl=0;
	for(ri int i=1;i<=n;i++)
	{
		while(hd<tl && Slope(sta[hd+1],sta[hd])<=2.0*a[i])hd++;
		F[i]=F[sta[hd]]+(a[i]-a[sta[hd]]+1)*(a[i]-a[sta[hd]]+1)-w;
		G[i]=G[sta[hd]]+1;
		while(hd<tl && Slope(sta[tl],sta[tl-1])>=Slope(i,sta[tl])) tl--;
		sta[++tl]=i;
	}
	return G[n];
}
signed main()
{
	n=read(), m=read();
	for(ri int i=1;i<=n;i++) a[i]=read(), a[i]+=a[i-1];
	int L=-1e16, R=0;
	while(L<=R)
	{
		int mid=(L+R)/2;
		if(Check(mid)>=m) Ans=F[n]+mid*m, R=mid-1;
		else L=mid+1;
	}
	printf("%lld\n",Ans);
	return 0;
}
posted @ 2021-04-01 11:28  zkdxl  阅读(55)  评论(0编辑  收藏  举报