zoukankan      html  css  js  c++  java
  • BZOJ4709 Jsoi2011 柠檬【决策单调性+单调栈】

    Description

    Flute 很喜欢柠檬。它准备了一串用树枝串起来的贝壳,打算用一种魔法把贝壳变成柠檬。贝壳一共有 N (1 ≤ N ≤ 100,000) 只,按顺序串在树枝上。为了方便,我们从左到右给贝壳编号 1..N。每只贝壳的大小不一定相同,贝壳 i 的大小为 si(1 ≤ si ≤10,000)。变柠檬的魔法要求,Flute 每次从树枝一端取下一小段连续的贝壳,并选择一种贝壳的大小 s0。如果 这一小段贝壳中 大小为 s0 的贝壳有 t 只,那么魔法可以把这一小段贝壳变成 s0t^2 只柠檬。Flute 可以取任意多次贝壳,直到树枝上的贝壳被全部取完。各个小段中,Flute 选择的贝壳大小 s0 可以不同。而最终 Flute 得到的柠檬数,就是所有小段柠檬数的总和。Flute 想知道,它最多能用这一串贝壳变出多少柠檬。请你帮忙解决这个问题。

    Input

    第 1 行:一个整数,表示 N。
    第 2 .. N + 1 行:每行一个整数,第 i + 1 行表示 si。

    Output

    仅一个整数,表示 Flute 最多能得到的柠檬数。

    Sample Input

    5
    2
    2
    5
    2
    3

    Sample Output

    21
    //Flute 先从左端取下 4 只贝壳,它们的大小为 2, 2, 5, 2。选择 s0 = 2,那么这一段里有 3 只大小为 s0 的贝壳,通过魔法可以得到 2×3^2 = 18 只柠檬。再从右端取下最后一只贝壳,通过魔法可以得到 1×3^1 = 3 只柠檬。总共可以得到 18 + 3 = 21 只柠檬。没有比这更优的方案了。


    思路

    首先发现一个性质
    对于选出来的每个区间,区间左端点种类和区间右端点相等,而且这一段区间选定的一定是端点的值,否则一定可以分离出没有用的区间另外计算贡献
    然后我们发现对于任意的(j_1<j_2<i_1<i_2)
    因为每一个种类的前缀和是单调递增的,且(y=x^2)是个下凸函数,所以如果在(i_1)(j_1)(j_2)更优,有(i_2)的时候(j_1)一定比(j_2)
    所以就可以对每个种类维护一个单调栈
    这样就可以维护最优的决策点
    但是为了保证决策和斜率都是单调的,在加入节点前我们可以判断,如果栈顶-1超过栈顶的最小前缀大小小于等于站顶超过当前节点的最小前缀大小,那么栈顶是无效的
    然后在加入之后判断栈顶-1超过栈顶的前缀大小小于等于当前前缀最小,那么栈顶已经不优秀了,直接弹栈就可以了


    //Author: dream_maker
    #include<bits/stdc++.h>
    using namespace std;
    //----------------------------------------------
    //typename
    typedef long long ll;
    //convenient for
    #define for_up(a, b, c) for (int a = b; a <= c; ++a)
    #define for_down(a, b, c) for (int a = b; a >= c; --a)
    #define for_vector(a, b) for (int a = 0; a < (signed)b.size(); ++a)
    //inf of different typename
    const int INF_of_int = 1e9;
    const ll INF_of_ll = 1e18;
    //fast read and write
    template <typename T>
    void Read(T &x) {
      bool w = 1;x = 0;
      char c = getchar();
      while (!isdigit(c) && c != '-') c = getchar();
      if (c == '-') w = 0, c = getchar();
      while (isdigit(c)) {
        x = (x<<1) + (x<<3) + c -'0';
        c = getchar();
      }
      if (!w) x = -x;
    }
    template <typename T>
    void Write(T x) {
      if (x < 0) {
        putchar('-');
        x = -x; 
      }
      if (x > 9) Write(x / 10);
      putchar(x % 10 + '0');
    }
    //----------------------------------------------
    const int N = 1e5 + 10;
    const int M = 1e4 + 10;
    ll dp[N];
    int n, a[N];
    int pre[N], last[N], s[N];
    vector<int> p[M];
    ll cal(int lastpos, int vl) {
      return dp[lastpos - 1] + 1ll * a[lastpos] * vl * vl;
    }
    int check(int x, int y) { // 算x点超过y点的最小前缀和
      int l = 1, r = n, ans = n + 1;
      while (l <= r) {
        int mid = (l + r) >> 1;
        if(cal(x, mid - s[x] + 1) >= cal(y, mid - s[y] + 1)) ans = mid , r = mid - 1;
        else l = mid + 1;
      }
      return ans;
    }
    #define S(s) (signed)s.size()
    #define R1(s) S(s) - 1
    #define R2(s) S(s) - 2
    #define now p[a[i]]
    int main() {
      Read(n);
      for_up(i, 1, n) {
        Read(a[i]);
        last[i] = pre[a[i]];
        s[i] = s[last[i]] + 1;
        pre[a[i]] = i;
      }
      for_up(i, 1, n) {
        while (S(now) > 1 && check(now[R2(now)], now[R1(now)]) <= check(now[R1(now)], i)) now.pop_back();
        now.push_back(i);
        while (S(now) > 1 && check(now[R2(now)], now[R1(now)]) <= s[i]) now.pop_back();
        dp[i] = cal(now[R1(now)], s[i] - s[now[R1(now)]] + 1ll);
      }
      Write(dp[n]);
      return 0;
    }
    
  • 相关阅读:
    angular中transclude的理解
    node express中使用static的错误
    待研究———node中使用session时的id不断更改问题
    node 中mongoose使用validate和密码加密的问题
    exports 和 module.exports 的区别
    node.js开发错误——DeprecationWarning: Mongoose: mpromise
    Mongoose全面理解
    【js】JSON.stringify 语法实例讲解
    springboot+mybatis使用PageHelper分页
    连接mysql提示Establishing SSL connection without server's identity verification is not recommended错误
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9732656.html
Copyright © 2011-2022 走看看