APIO2014序列分割bzoj3675

最开始输出98,最开始赶脚是窝姿势不对……偷偷瞄了一眼黄学长的blog感觉get了新姿势,怒码,然后还是98……最后居然是窝括号的位置放错了……过于隐蔽肉眼没能debug出来QAQQQQQQQQQQ……还是太弱了……

这题很显然窝们可以写出Dp方程分程f[i]=f[j]+s[j]*(s[i]-s[j]),因为这题实际上和切的顺序是无关的,其实这很显然证明就是对于答案中的每一个区间必然会被乘上k?次(随便脑补了一下并没有实际计算),然后窝们就可以灰常高兴地写斜率优化啦

当年斜率DP专题比赛怒AK的RP哪去了QAQQQQQQQQQQQ

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define LL long long
using namespace std;
LL sum[N],f[N],q[N],a[N],g[N];
int n,k;

LL read()
{
	LL x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x;
}

double calc(int j,int k)
{
        return (double)(sum[k]*sum[k]-sum[j]*sum[j]-g[k]+g[j])/(double)(sum[k]-sum[j]);
}

void work(int x)
{
        int h=1,t=0;
        for (int i=x;i<=n;i++)
        {
                while (h<t&&calc(q[t-1],q[t])>calc(q[t],i-1)) t--;q[++t]=i-1;
                while (h<t&&calc(q[h],q[h+1])<sum[i]) h++;int p=q[h];
                f[i]=g[p]+(sum[i]-sum[p])*sum[p];
        }
        for (int i=x;i<=n;i++) swap(f[i],g[i]);
}

void dp()
{
        for (int i=1;i<=k;i++)
                work(i);
        printf("%lld\n",g[n]);
}

int main()
{
        n=read();k=read();
        for (int i=1;i<=n;i++)
                a[i]=read();
        int top=0;
	for(int i=1;i<=n;i++)
                if(a[i]!=0)
        a[++top]=a[i];
	n=top;
	for(int i=1;i<=n;i++)
		sum[i]=sum[i-1]+a[i];
	dp();
	return 0;
}


你可能感兴趣的:(dp)