[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) Fi,p=min(Fj,p1+(aiaj+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+(aiak+1)2Fj,p+(aiaj+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,p2aiak+1+ak+12Fj,p2aiaj+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,pFj,p+ak+12aj+122ai(ak+1aj+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+1aj+1Fk,pFj,p+ak+12aj+122ai

此时, 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(qtail1,qtail)calc(qtail,im)

反之,

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(qtail1,qtail)calc(qtail,im)

踢去队尾。

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值