[HDU3480]Division

题面描述

传送门

思路

排序之后显而易见的状态转移方程

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)

决策单调性

自己证明。

踢队头

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

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

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})

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

此时, k k 优于 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)

反之,

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)

踢去队尾。

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;
}

猜你喜欢

转载自blog.csdn.net/zyszlb2003/article/details/94553960