• SDOI2016 征途 题解


    由于暂时不太想写四边形不等式的题解,于是就暂时写篇斜率优化的题解(反正迟早都得写)

    大概意思说给你\(n\)段连续区间,你可以将这些区间分割成\(m\)块(不能将原有区间分成两块,只能在交界处分割),使得\(m\)块区间的长度的方差和最小。题目要求,若设方差为\(v\),那么最后请输出\(v*m^2\)。由此可见我们最小化的就是\(v*m^2\),那么我们可以对这个式子做点手脚,设\(m\)段区间的长度分别为\(s[1]、s[2]、s[3]...s[m]\),长度之和为\(Sn\),则有:

    \(v*m^2\)
    \(=m^2*\frac{1}{m}*\sum_{i=1}^{m}(s[i]-\frac{Sn}{m})^2\)
    \(=m*\sum_{i=1}^{m}(s[i]-\frac{Sn}{m})^2\)
    \(=m*\sum_{i=1}^{m}(s[i]^2-\frac{2*s[i]*Sn}{m}+(\frac{Sn}{m})^2)\)

    接着我们把三项都提出来,那么原式可化为:

    \((m*\sum_{i=1}^{m}s[i]^2)-(m*\sum_{i=1}^{m}\frac{2*s[i]*Sn}{m})+(m*\sum_{i=1}^{m}\frac{Sn^2}{m^2})\)
    \(=(m*\sum_{i=1}^{m}s[i]^2)-m*\frac{2*Sn^2}{m}+m*\frac{Sn^2}{m}\)
    \(=(m*\sum_{i=1}^{m}s[i]^2)-Sn^2\)

    所以将结果最小化就是将\(\sum_{i=1}^{m}s[i]^2\)最小化。那么我们设\(w[i]\)\(s[i]\)数组的前缀和,接下来就可以动态规划了,设\(dp[i][j]\)为前\(i\)段区间被分割成\(j\)块的最小代价,那么套路的\(dp\)方程如下:

    \(dp[i][j]=min\left\{dp[i-1][k]+(w[j]-w[k])^2\right\},1\le{i}\le{n},1\le{j}\le{m},0\le{k}<j\)

    然后开始化斜率优化的式子。由于这样的做法是\(O(n^3)\)的,所以我们考虑把\(k\)那维优化掉。因为是求最小值,所以要求截距尽量小,因此斜率应该呈递增的状态,故下凸,所以我们用单调队列来维护一个下凸包。然后,什么时候\(k1\)就没有\(k2\)优了呢?我们假设\(k1<k2\)并且\(k1\)优于\(k2\),并且设\(t=i-1\),那么有如下的式子:

    \(dp[t][k1]+(w[j]-w[k1])^2<dp[t][k2]+(w[j]-w[k2])^2\)
    \(\therefore dp[t][k1]+w[j]^2-2*w[j]*w[k1]+w[k1]^2<dp[t][k2]+w[j]^2-2*w[j]*w[k2]+w[k2]^2\)
    \(\therefore dp[t][k1]-2*w[j]*w[k1]+w[k1]^2<dp[t][k2]-2*w[j]*w[k2]+w[k2]^2\)
    \(\therefore 2*w[j]*w[k2]-w[k2]^2-2*w[j]*w[k1]+w[k1]^2<dp[t][k2]-dp[t][k1]\)
    \(\therefore 2*w[j]*(w[k2]-w[k1])-w[k2]^2+w[k1]^2<dp[t][k2]-dp[t][k1]\)
    \(\therefore 2*w[j]*(w[k2]-w[k1])<dp[t][k2]-dp[t][k1]+w[k2]^2-w[k1]^2\)
    \(\therefore dp[t][k2]-dp[t][k1]+w[k2]^2-w[k1]^2>2*w[j]*(w[k2]-w[k1])\)
    \(\therefore \frac{dp[t][k2]-dp[t][k1]+w[k2]^2-w[k1]^2}{w[k2]-w[k1]}>2*w[j]\)

    有了这个式子后我们就可以知道,当\(\frac{dp[t][k2]-dp[t][k1]+w[k2]^2-w[k1]^2}{w[k2]-w[k1]}>2*w[j]\)\(k1\)更优,反之\(k2\)更优,于是斜率优化一般套路求出最优值\(dp[n][m]\),然后再回归到上方:\(v*m^2=(m*\sum_{i=1}^{m}s[i]^2)-Sn^2\),所以\(m*dp[n][m]-Sn^2\)就是最终的答案,上代码:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int N=3e3+10;
    int n,m,a[N],head,tail,q[N];
    ll sum,dp[N][N],p[N];
    ll GetY(int t,int k1,int k2){return dp[t][k2]-dp[t][k1]+p[k2]*p[k2]-p[k1]*p[k1];}
    ll GetX(int k1,int k2){return p[k2]-p[k1];}
    int main(){
        scanf("%d%d",&n,&m);
        for(register int i=1;i<=n;i++)scanf("%d",&a[i]),sum+=a[i];sum*=sum;
        for(register int i=1;i<=n;i++)p[i]=p[i-1]+a[i];
        memset(dp,127,sizeof(dp));dp[0][0]=0;
        for(register int i=1;i<=m;i++){
            head=tail=0;
            for(register int j=1;j<=n;j++){
                while(head<tail&&GetY(i-1,q[head],q[head+1])<=2*p[j]*GetX(q[head],q[head+1]))head++;
                dp[i][j]=dp[i-1][q[head]]+(p[j]-p[q[head]])*(p[j]-p[q[head]]);
                while(head<tail&&GetY(i-1,q[tail-1],q[tail])*GetX(q[tail],j)>=GetY(i-1,q[tail],j)*GetX(q[tail-1],q[tail]))tail--;
                q[++tail]=j;
            }
        }
        printf("%lld\n",(ll)m*dp[m][n]-sum);
        return 0;
    }
    
  • 相关阅读:
    POJ 1680 Fork() Makes Trouble
    课堂改进意见
    梦断代码 读后感3
    梦断代码 读后感2
    找一问题
    软件评价——搜狗输入法
    《梦断代码》读后感1
    站立会议第十天
    站立会议第九天
    站立会议第八天
  • 原文地址:https://www.cnblogs.com/ForwardFuture/p/9252159.html
Copyright © 2020-2023  润新知