题面描述
思路
排序之后显而易见的状态转移方程
F i , p = min ( F j , p − 1 + ( a i − a j + 1 ) 2 ) F_{i,p}=\min(F_{j,p-1}+(a_i-a_{j+1})^2) Fi,p=min(Fj,p−1+(ai−aj+1)2)
决策单调性
自己证明。
踢队头
F k , p + ( a i − a k + 1 ) 2 ≤ F j , p + ( a i − a j + 1 ) 2 F_{k,p}+(a_i-a_{k+1})^2\le F_{j,p}+(a_i-a_{j+1})^2 Fk,p+(ai−ak+1)2≤Fj,p+(ai−aj+1)2
F k , p − 2 ∗ a i ∗ a k + 1 + a k + 1 2 ≤ F j , p − 2 ∗ a i ∗ a j + 1 + a j + 1 2 F_{k,p}-2*a_i*a_{k+1}+{a_{k+1}}^2\le F_{j,p}-2*a_i*a_{j+1}+{a_{j+1}}^2 Fk,p−2∗ai∗ak+1+ak+12≤Fj,p−2∗ai∗aj+1+aj+12
F k , p − F j , p + a k + 1 2 − a j + 1 2 ≤ 2 ∗ a i ∗ ( a k + 1 − a j + 1 ) F_{k,p}-F_{j,p}+{a_{k+1}}^2-{a_{j+1}}^2\le2*a_i*(a_{k+1}-a_{j+1}) Fk,p−Fj,p+ak+12−aj+12≤2∗ai∗(ak+1−aj+1)
c a l c ( j , k ) = F k , p − F j , p + a k + 1 2 − a j + 1 2 a k + 1 − a j + 1 ≤ 2 ∗ a i calc(j,k)=\frac{F_{k,p}-F_{j,p}+{a_{k+1}}^2-{a_{j+1}}^2}{a_{k+1}-a_{j+1}}\le2*a_i calc(j,k)=ak+1−aj+1Fk,p−Fj,p+ak+12−aj+12≤2∗ai
此时, k k k优于 j j j
可以发现,队列应维护一个下凸壳。
踢队尾
由于维护一个下凸壳,
所以
c a l c ( q t a i l − 1 , q t a i l ) ≤ c a l c ( q t a i l , i − m ) calc(q_{tail-1},q_{tail})\le calc(q_{tail},i-m) calc(qtail−1,qtail)≤calc(qtail,i−m)
反之,
当
c a l c ( q t a i l − 1 , q t a i l ) ≥ c a l c ( q t a i l , i − m ) calc(q_{tail-1},q_{tail})\ge calc(q_{tail},i-m) calc(qtail−1,qtail)≥calc(qtail,i−m)
踢去队尾。
AC code
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<cstring>
#define ll long long
#define gc getchar()
using namespace std;
const int N=1e4+10;
const int M=5e3+10;
inline void qr(int &x)
{
x=0;char c=gc;int f=1;
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;
}
void qw(int x)
{
if(x<0)x=-x,putchar('-');
if(x/10)qw(x/10);
putchar(x%10+48);
}
int f[N],g[N],a[N];
int q[N],l,r;
inline int calc(int j,int k)
{
return g[k]-g[j]+a[k+1]*a[k+1]-a[j+1]*a[j+1];
}
inline int sum(int j,int k)
{
return a[k+1]-a[j+1];
}
bool pd(int i,int j,int k)
{
return calc(j,i)*sum(k,j)<=calc(k,j)*sum(j,i);
}
int main()
{
int t;scanf("%d",&t);int T=0;
while(t--)
{
++T;
int n,m;scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)qr(a[i]);
sort(a+1,a+n+1);
int tot=0;
for(int i=1;i<=n;i++)if(!tot||a[tot]!=a[i])a[++tot]=a[i];
n=tot;
for(int i=1;i<=n;i++)f[i]=(a[i]-a[1])*(a[i]-a[1]);
for(int i=2;i<=m;i++)
{
l=1;r=1;q[1]=i-1;memcpy(g,f,sizeof(g));
for(int j=i;j<=n;j++)
{
while(l<r&&calc(q[l],q[l+1])<=2*a[j]*sum(q[l],q[l+1]))++l;
f[j]=g[q[l]]+(a[j]-a[q[l]+1])*(a[j]-a[q[l]+1]);
while(l<r&&pd(j,q[r],q[r-1]))--r;
q[++r]=j;
}
}
printf("Case %d: %d\n",T,f[n]);
}
return 0;
}