CS Academy Round 70 Squared Ends dp+cdq partition+convex hull+dichotomy

meaning of the title

Given a sequence a [1.n] of length n, we now divide this sequence into k segments, and the contribution of section i [li,ri][li,ri] is (a[ri] a[li])2(a[ri] a[li])2(a[ri] a [li]). Minimizing the sum of contributions for each paragraph is required.
n<=10000,k<=100

Analysis

Let dp[i,j]dp[i,j] denote the minimum contribution sum of the first I positions divided into J segments.
It is not difficult to transfer:

dp[i,j]=min(dp[k,j−1]+(a[i]−a[k+1])2)dp[i,j]=min(dp[k,j−1]+(a[i]−a[k+1])2)

After splitting the square
dp[i,j]=min(dp[k,j−1]+a[i]2+a[k+1]2−2∗a[i]∗a[k+1])dp[i,j]=min(dp[k,j−1]+a[i]2+a[k+1]2−2∗a[i]∗a[k+1])

If we take this transfer as the minimum of the ordinates of a point in a bunch of straight lines, it is not difficult to find that the answer must be on the convex hull formed by these straight lines.
We can divide and conquer cdq by recursing [l,mid], building the convex hull of [l,mid], transferring it to [mid+1,r], and then recursing [mid+1,r] downward.

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define MIN(x,y) x=min(x,y)
using namespace std;

typedef long long LL;

const int N=10005;
const LL inf=(LL)1e17;

int n,m,a[N],que[N];
LL f[N][105];
struct line{LL k,b;}p[N];

bool cmp(line a,line b)
{
    return a.k>b.k||a.k==b.k&&a.b<b.b;
}

double get_pts(line x,line y)
{
    return (double)(y.b-x.b)/(x.k-y.k);
}

void solve(int l,int r)
{
    if (l==r) return;
    int mid=(l+r)/2;
    solve(l,mid);
    for (int j=1;j<m;j++)
    {
        int tot=0;
        for (int i=l;i<=mid;i++) p[++tot].k=-2*a[i+1],p[tot].b=(LL)a[i+1]*a[i+1]+f[i][j];
        sort(p+1,p+tot+1,cmp);
        int top=0;
        for (int i=1;i<=tot;i++)
        {
            if (i>1&&p[i].k==p[i-1].k) continue;
            while (top>1&&get_pts(p[i],p[que[top-1]])<=get_pts(p[que[top]],p[que[top-1]])) top--;
            que[++top]=i;
        }
        for (int i=mid+1;i<=r;i++)
        {
            int L=1,R=top-1;
            while (L<=R)
            {
                int mid=(L+R)/2;
                if ((LL)p[que[mid+1]].k*a[i]+p[que[mid+1]].b<(LL)p[que[mid]].k*a[i]+p[que[mid]].b) L=mid+1;
                else R=mid-1;
            }
            MIN(f[i][j+1],(LL)p[que[L]].k*a[i]+p[que[L]].b+(LL)a[i]*a[i]);
        }
    }
    solve(mid+1,r);
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            f[i][j]=inf;
    for (int i=1;i<=n;i++) f[i][1]=(LL)(a[i]-a[1])*(a[i]-a[1]);
    solve(1,n);
    printf("%lld",f[n][m]);
    return 0;
}

Posted by hernan on Wed, 06 Feb 2019 09:51:16 -0800