zoukankan      html  css  js  c++  java
  • bzoj1911 [Apio2010]特别行动队

    1911: [Apio2010]特别行动队

    Time Limit: 4 Sec  Memory Limit: 64 MB
    Submit: 5224  Solved: 2571
    [Submit][Status][Discuss]

    Description

    Input

    Output

    Sample Input

    4
    -1 10 -20
    2 2 3 4

    Sample Output

    9

    HINT

    分析:dp斜率优化的裸题.
       令f(i)表示分到第i个人的最大值,那么f(i) = max{f(j) + a * (sum_i - sum_j)^2 + b * (sum_i - sum_j) + c}. 
      把式子展开一下,可以得到:f(i) = max{-2a*sum_i*sum_j + f(j) + a * sum_j ^ 2 - b * sum_j + a * sum_i ^ 2 + b * sum_i + c}. 
      这其实是把关于i的部分放到了右边,与j有关的放到了左边. 右边这种和j无关的可以认为是常数(一下子就能知道.),可以提到括号外面:f(i) = max{-2a * sum_i * sum_j + f(j) + a * sum_j ^ 2 - b * sum_j} + a * sum_i^2 + b * sum_i + c. 
      可以发现如果j固定了,那么这个式子的取值是和sum_i相关的,可以令sum_i为自变量x,-2a * sum_j为斜率k,f(j) + a * sum_j ^ 2 - b * sum_j为截距b. 那么f(i) = kx + b + a * sum_i^2 + b * sum_i + c.
      问题的关键就是如何找到一个j,使得kx + b尽量大. 维护一个双端单调队列,里面存的是直线.当枚举到新的点时,先看队首的两条直线,如果第l条在sum_i的位置的纵坐标≤第l+1条在sum_i的位置的纵坐标.那么第l条就可以丢啦!why? 随着i的增大,sum_i是不降的,那么斜率也是不降的,既然纵坐标已经≤了,并且斜率也≤,那么这条直线以后肯定不会带来贡献,丢掉!
      接着取出队首元素j,j对应的k和b就用来更新f(i).
      然后看队尾的两个元素r-1,r.如果直线r被直线i和直线r-1完全覆盖了,也就是不可能取到最大值,那么就丢掉.怎么判断是否被完全覆盖呢?如果令r-1和i的交点的横坐标为x,纵坐标为y,如果直线r在横坐标为x时纵坐标≤y了,那么就被完全覆盖了,丢掉即可.
      最后加入i.
      这个单调队列的写法和平时习惯的写法有一点点区别:l = 1,r = 0 变成了 l = 0,r = 0; l <= r变成了l < r:
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    const ll maxn = 1000010;
    ll n,a,b,c,sum[maxn],q[maxn * 2],l,r,f[maxn];
    
    ll K(ll i)
    {
        return -2 * a * sum[i];
    }
    
    ll B(ll i)
    {
        return f[i] + a * sum[i] * sum[i] - b * sum[i];
    }
    
    ll Y(ll i,ll j)
    {
        return sum[j] * K(i) + B(i);
    }
    
    bool check(int y1,int y2,int y3)
    {
        ll temp1 = (K(y1) - K(y3)) * (B(y2) - B(y1));
        ll temp2 = (K(y1) - K(y2)) * (B(y3) - B(y1));
        return temp1 <= temp2;
    }
    
    int main()
    {
        scanf("%lld",&n);
        scanf("%lld%lld%lld",&a,&b,&c);
        for (int i = 1; i <= n; i++)
        {
            scanf("%lld",&sum[i]);
            sum[i] += sum[i - 1];
        }
        l = 0,r = 0;
        for (int i = 1; i <= n; i++)
        {
            while (l < r && Y(q[l],i) <= Y(q[l + 1],i))
                l++;
            f[i] = Y(q[l],i) + a * sum[i] * sum[i] + b * sum[i] + c;
            while (l < r && check(i,q[r],q[r - 1]))
                r--;
            q[++r] = i;
        }
        printf("%lld
    ",f[n]);
    
        return 0;
    }
  • 相关阅读:
    Birt报表存储过程多选参数的设置
    jQuery UI AutoComplete的使用
    关于事件的简单优化
    Java编程思想(Chapter2、4、6)
    CSS层模型
    [转]Java并发编程:Lock
    Java多线程synchronized同步
    关于Thread.currentThread()和this的差异
    关于JavaScript闭包的小问题
    ReactiveCocoa(二)
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8470974.html
Copyright © 2011-2022 走看看