zoukankan      html  css  js  c++  java
  • hdu3507 print article

    hdu3507 print ariticle 斜率优化入门题

    一个人要用n((0le nle 500005))个单词打一篇文章。一个单词有一个价值(C_i),一个文章可以分成若干段,每一段消耗价值((sum_{i=1}^kC_i)^2+M),其中M是常数。问如何安排单词打法,使得消耗的价值最小?

      首先,dp方程是(dp[i]=dp[j]+M+(sum[i]-sum[j])^2)

      但是,暴力转移是(O(n^2))的。单调队列优化也用不了,因为dp方程内还有与i*j相关的项。咋办呢?当然是用斜率优化了!(什么鬼)

      来转换一下dp方程。假设目前dp的是i,j,k是能转移到i的阶段。如果从j转移比从k更优,说明:(dp[j]+M+(sum[i]-sum[j])^2<dp[k]+M+(sum[i]-sum[k])^2)

      推一下式子,可以推成(dp[i]-dp[k]+2sum[i]sum[k]-2sum[i]sum[j]+sum[j]^2-sum[k]^2<0)。为了不改变不等式的符号,我们让(sum[j]>sum[k])。那么把式子除上(2(sum[j]-sum[k])),得到:(frac{dp[j]+sum[j]^2-(dp[k]+sum[k]^2)}{2(sum[j]-sum[k])}<sum[i]),拿x和y数组代一下,(frac{y[j]-y[k]}{x[j]-x[k]}<sum[i] (x[j]-x[k]>0))

      现在我们来边缘op。首先,x[j]和y[j]都只和j本身有关,与转移无关。(frac{y[j]-y[k]}{x[j]-x[k]}<sum[i])中,我们发现只有sum[i]和i有关。我们成功分离出了与i有关的项!同时(frac{y[j]-y[k]}{x[j]-x[k]})很像斜率的表示,所以试着把所有((x[j], y[j]))画到坐标系上:

      图片

      对于一个点,它左上角的所有点,无论如何都会比它劣(边的斜率为负),同理,在它右下角的所有点,无论如何都会比它优。其它位置的点则不一定。因此,只有半下凸包上的点有可能是最优点。

      所以在转移过程中,我们要动态维护一个下凸包。每次决策的时候,在凸包上面二分就行了。

      但是!这道题很神奇。它满足决策单调性,并且新加入的点一定在所有点的右边。所以你既不用动态维护下凸包,也不用在凸包上面二分。用单调队列就行啦!

      看似这道题做完了,然而还有个坑点:(c_i)可能是0…这说明可能有多个点的x下标相同,甚至有可能多个点重合!当有多个点的x下标相同时,(x[j]-x[k]=0),也就是说如果你算斜率会被零除,然后光荣RE。所以计算斜率应该乘出来,避免这种情况。而当有多个点重合时,(y[j]-y[k]=0<sum[i]),也就是队列不会往前推。然而重合点不一定是最好的。所以在写程序的时候应该是小于等于。记住开始的式子(dp[i]=dp[j]+M+(sum[i]-sum[j])^2),斜率式是我们推出来的,只不过方便计算,还有一些情况被漏掉了!

    #include <cstdio>
    using namespace std;
    
    const int maxn=5e5+5;
    int n, m, h, t, sum[maxn];
    int dp[maxn], x[maxn], y[maxn], q[maxn];
    inline int sqr(int x){ return x*x; }
    
    int main(){
        while (~scanf("%d%d", &n, &m)){
            for (int i=1; i<=n; ++i) scanf("%d", &sum[i]);
            for (int i=2; i<=n; ++i) sum[i]+=sum[i-1];
            sum[0]=h=t=dp[0]=x[0]=y[0]=0;
            for (int i=1; i<=n; ++i){
                while (h<t&&y[q[h+1]]-y[q[h]]
                       <=sum[i]*(x[q[h+1]]-x[q[h]])) ++h;
                dp[i]=dp[q[h]]+m+sqr(sum[i]-sum[q[h]]);
                x[i]=2*sum[i], y[i]=dp[i]+sqr(sum[i]);
                while (h<t&&(y[i]-y[q[t]])*(x[q[t]]-x[q[t-1]])
                       <=(y[q[t]]-y[q[t-1]])*(x[i]-x[q[t]])) --t;
                q[++t]=i;
            }
            printf("%d
    ", dp[n]);
        }
        return 0;
    }
    

      看完记得点个赞!

  • 相关阅读:
    DTS和AC3的区别
    bind出现 file does not end with newline错误
    删除桌面菜单多余项
    you have requested a nonexistent service "grid"
    php 常用函数
    Jquery
    Twig 的Filters学习
    Twig 的 tags学习(中文) 之三 完结
    PHP 正则表达式
    SQL处理字符串
  • 原文地址:https://www.cnblogs.com/MyNameIsPc/p/7611156.html
Copyright © 2011-2022 走看看