zoukankan      html  css  js  c++  java
  • 来点斜率优化?

    前置芝士

    • 单调队列
    • 会写方程
    • 初中数学基础
    • 斜率优化原理

    大多数斜率优化真的都是套路
    学之前以为比较难,会用之后发现主要的难点还是在转移方程,这种优化啥的,就推推式子,然后结合单调队列,优化复杂度

    建议大家回了思想以后还是要多自己手推一下,化两个式子就差不多了
    至于一开始的DP方程,就看经验和造化了

    原理介绍

    本来就打算只写题,所以这部分内容先咕咕了

    [ZJOI2007]仓库建设

    我们设 \(f_i\) 表示在 \(i\) 处建立工厂所需的最小花费,显然要枚举一个上一次建厂的位置 \(j\),根据题意很容易得出转移方程

    \[\large f_i = \min \{ f_j + \sum_{k = j + 1}^{i} P_k(x_i - x_k) \} \]

    其中 \(P_x\) 表示 \(x\) 处的物品数,\(x_i\) 表示 \(i\) 点到一号点的距离

    对这个式子进行化简

    \[\large f_i = \min \{ f_j + x_i \sum_{k = j + 1}^{i} P_k - \sum_{k = j + 1}^{i} P_k x_k \} \]

    发现两个求和式子都可以用前缀和预处理,所以设 \(sum_x = \sum_{i = 1}^{x} P_i\)\(val_x = \sum_{i = 1}^{x} P_ix_i\),得

    \[\large f_i = f_j + x_i (sum_i - sum_j) - (val_i - val_j) + cost_i \]

    \(j\)\(k\) 优,那么

    \[\large f_j - x_i sum_j + val_j \le f_k - x_i sum_k + val_k \]

    \[\large x_i (sum_k - sum_j) \le (f_k + val_k) - (f_j + val_j) \]

    \(F_x = f_x + val_x\),则

    \[\large x_i (sum_k - sum_j) \le F_k - F_j \]

    \[\large x_i \le \frac{F_k - F_j}{sum_k - sum_j} \]

    所以说只要 $ \large x_i > \frac{F_k - F_j}{sum_k - sum_j}$ 单调队列中所维护的答案就不合法,将不合法元素从队首弹出即可

    维护一个单调递增队列即可

    \(K(x_1, x_2) = \frac{F_{x_1} - F_{x_2}}{sum_{x_1} - sum_{x_2}}\)

    那么不合法条件是 \(K(q_{tail - 1}, q_{tail}) > K(q_{tail - 1}, pos)\),(\(pos\) 是新加入的点)

    代码也非常好写

    /*
    Work by: Suzt_ilymics
    Knowledge: ??
    Time: O(??)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define LL long long
    #define int long long
    #define orz cout<<"lkp AK IOI!"<<endl
    
    using namespace std;
    const int MAXN = 1e6+5;
    const int INF = 1e9+7;
    const int mod = 1e9+7;
    
    int n;
    int dis[MAXN], a[MAXN], cost[MAXN];
    int sum[MAXN], val[MAXN];
    int f[MAXN];
    int q[MAXN], head = 1, tail = 0;
    
    int read(){
        int s = 0, f = 0;
        char ch = getchar();
        while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
        while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
        return f ? -s : s;
    }
    
    int F(int x) { return f[x] + val[x]; }
    
    double K(int x_1, int x_2) {
        if(sum[x_1] == sum[x_2]) return F(x_1) > F(x_2) ? 1e9 : -1e9;
        return (F(x_1) - F(x_2)) / (sum[x_1] - sum[x_2]);
    }
    
    void Delete(int pos){ while(head < tail && dis[pos] > K(q[head], q[head + 1])) head ++; }
    
    void Insert(int pos) {
        while(head < tail && K(q[tail - 1], q[tail]) > K(q[tail - 1], pos)) tail --;
        q[++ tail] = pos;
    }
    signed main()
    {
        n = read();
        for(int i = 1; i <= n; ++i) dis[i] = read(), a[i] = read(), cost[i] = read();
        for(int i = 1; i <= n; ++i) {
            sum[i] = sum[i - 1] + a[i];
            val[i] = val[i - 1] + a[i] * dis[i];
        }
        Insert(0);
        for(int i = 1; i <= n; ++i) {
            Delete(i);
            int j = q[head];
            f[i] = f[j] + dis[i] * (sum[i] - sum[j]) - val[i] + val[j] + cost[i];
            Insert(i);
        }
        printf("%lld", f[n]);
        return 0;
    }
    
    

    P2365 任务安排

    Description

    题面:P2365 任务安排

    Solution

    \(f_i\) 表示到第 \(i\) 个任务所需的最少花费,则有转移方程
    有一说一我切完这道题才读懂了题面

    \[f_i = f_j + t_i(c_i - c_j) + S(c_n - c_j) \]

    其中 \(t_i\) 表示所需时间的前缀和, \(c_i\) 表示费用系数的前缀和

    同样是设 \(j\)\(k\) 优,则

    \[f_j + t_i(c_i - c_j) + S(c_n - c_j) \le f_k + t_i(c_i - c_k) + S(c_n - c_k) \]

    整理得

    \[t_ic_k - t_ic_j \le (f_k - Sc_k) - (f_j - Sc_j) \]

    \(F(x) = f_x - Sc_x\),那么

    \[t_i \le \frac{F(k) - F(j)}{c_k - c_j} \]

    然后就和上面一个套路了

    Code

    /*
    Work by: Suzt_ilymics
    Knowledge: ??
    Time: O(??)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define LL long long
    #define int long long
    #define orz cout<<"lkp AK IOI!"<<endl
    
    using namespace std;
    const int MAXN = 3e5+5;
    const int INF = 1e9+7;
    const int mod = 1e9+7;
    
    int n, S;
    int tim[MAXN], cost[MAXN];
    int t[MAXN], c[MAXN];
    int f[MAXN];
    int q[MAXN], head = 1, tail = 0;
    
    int read(){
        int s = 0, f = 0;
        char ch = getchar();
        while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
        while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
        return f ? -s : s;
    }
    
    double F(int x) {
        return f[x] - S * c[x];
    }
    
    double K(int x_1, int x_2) {
    //    if(c[x_2] == c[x_1]) return F(x_1) > F(x_2) ? 1e9 : -1e9;
        return (double)(F(x_2) - F(x_1)) / (c[x_2] - c[x_1]);
    }
    
    void Delete(int pos) {
    //    cout<<pos<<" "<<t[pos]<<" "<<K(q[head], q[head + 1])<<endl;
        while(head < tail && t[pos] > K(q[head], q[head + 1])) head ++;
    }
    
    void Insert(int pos) {
        while(head < tail && K(q[tail], q[tail - 1]) > K(q[tail - 1], pos)) tail --;
        q[++ tail] = pos;
    }
    
    signed main()
    {
        n = read(), S = read();
        for(int i = 1; i <= n; ++i) {
            tim[i] = read(), cost[i] = read();
            t[i] = t[i - 1] + tim[i], c[i] = c[i - 1] + cost[i];
        }
        Insert(0);
        for(int i = 1; i <= n; ++i) {
            Delete(i);
            int j = q[head];
            f[i] = f[j] + t[i] * (c[i] - c[j]) + S * (c[n] - c[j]);
            Insert(i);
        }
        printf("%lld", f[n]);
        return 0;
    }
    
    

    P3628 [APIO2010]特别行动队

    Description

    题面:P3628 [APIO2010]特别行动队

    Solution

    \(f_i\) 表示到第 \(i\) 个士兵所得到的最大战斗力,易得转移方程

    \[\large f_i = f_j + a(sum_i - sum_j)^2 + b(sum_i - sum_j) + c \]

    其中 \(sum_i\) 表示前 \(i\) 个士兵的战斗力之和

    让我们再次假设 \(j\)\(k\) 优,那么

    \[f_j + a(sum_i - sum_j)^2 + b(sum_i - sum_j) + c \ge f_k + a(sum_i - sum_k)^2 + b(sum_i - sum_k) + c \]

    化简得

    \[2 \times a \times sum_i \ge \frac{(f_k + a \times sum_k^2) - (f_j + a \times sum_j^2)}{sum_k - sum_j} \]

    \(F(x) = f_x + a \times sum_x^2 - b \times sum_x\)

    \[\large 2 \times a \times sum_i \ge \frac{F(k) - F(j)}{sum_{k} - sum_{j}} \]

    发现 \(a\) 是负数,所以就不移到右边了
    然后就是板子了,一个套路
    话说要是除过去一个负数咋整啊,不用变号?

    Code

    /*
    Work by: Suzt_ilymics
    Knowledge: ??
    Time: O(??)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define LL long long
    #define int long long
    #define orz cout<<"lkp AK IOI!"<<endl
    
    using namespace std;
    const int MAXN = 1e6+5;
    const int INF = 1e9+7;
    const int mod = 1e9+7;
    
    int n, a, b, c;
    int sum[MAXN], f[MAXN];
    int q[MAXN], head = 1, tail = 0;
    
    int read(){
        int s = 0, f = 0;
        char ch = getchar();
        while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
        while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
        return f ? -s : s;
    }
    
    int F(int x) { return f[x] + a * sum[x] * sum[x] - b * sum[x]; }
    double K(int x_1, int x_2) { return (F(x_1) - F(x_2)) / ((sum[x_1] - sum[x_2])); }
    void Delete(int pos) { while(head < tail && 2 * sum[pos] * a < K(q[head], q[head + 1])) head ++; }
    void Insert(int pos) {
        while(head < tail && K(q[tail - 1], q[tail]) < K(q[tail - 1], pos)) tail --;
        q[++ tail] = pos;
    }
    
    signed main()
    {
        n = read(), a = read(), b = read(), c = read();
        for(int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + read();
        Insert(0);
        for(int i = 1; i <= n; ++i) {
            Delete(i);
            int j = q[head], sum_ = sum[i] - sum[j];
            f[i] = f[j] + a * sum_ * sum_ + b * sum_ + c;
            Insert(i);
        }
        printf("%lld", f[n]);
        return 0;
    }
    
    

    #10191. 「一本通 5.6 练习 4」打印文章

    Description

    题面:#10191. 「一本通 5.6 练习 4」打印文章

    Solution

    \(f_i\) 表示前 \(i\) 个数的最小花费,根据题意可得转移方程

    \[\large f_i = \min \{ f_j + (\sum_{k = j + 1}^{i} c_k)^2 + M \} \]

    维护一个前缀和

    \[\large f_i = \min \{ f_j + (sum_i - sum_j)^2 + M \} \]

    \(j\)\(k\) 优,则

    \[\large f_j + (sum_i - sum_j)^2 + M \le f_k + (sum_i - sum_k)^2 + M \]

    进行化简,将与 \(i\) 有关的项移到左边,剩下的移到右边

    \(F(x) = f_x + sum_x^2\)

    \[2sum_i \le \frac{F(k) - F(j)}{sum_k - sum_j} \]

    进行斜率优化即可

    有个坑点

    来看一眼这个数据

    5 2
    9
    0
    5
    6
    9
    

    中间有个 \(0\), 那么 \(sum_1 = sum_2 = 9\)

    如果此时进行除法运算,因为分子相减可能为 0,所以计算机会运行出错,会返回一个极大值,但极大值不能显示,只能返回一个 nan 字样

    这时可以将它转化成整形,它就会因为自然溢出而变成一个极小值,可能恰好对答案是对的

    另外一种处理方式是判断分子是否为 0,然后比较分母的大小,通过判断返回一个极大值或者极小值

    Emm……,
    经过测试,对于上面这组样例返回极大值和极小值一个样,判断的符号是大于还是小于都不影响答案

    如果有大佬遇到返回极大值或极小值,或者大于号小于号对答案有影响的时候,望指出

    Code

    /*
    Work by: Suzt_ilymics
    Knowledge: ??
    Time: O(??)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define LL long long
    #define int long long
    #define orz cout<<"lkp AK IOI!"<<endl
    
    using namespace std;
    const int MAXN = 5e5+5;
    const int INF = 1e9+7;
    const int mod = 1e9+7;
    
    int n, m;
    int sum[MAXN], f[MAXN];
    int q[MAXN], head = 1, tail = 0;
    
    int read(){
        int s = 0, f = 0;
        char ch = getchar();
        while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
        while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
        return f ? -s : s;
    }
    
    LL F(int x) { return f[x] + sum[x] * sum[x]; }
    double K(int x_1, int x_2) { 
        if(sum[x_1] == sum[x_2]) return F(x_1) > F(x_2) ? 1e9 : -1e9;
        return (double)(F(x_1) - F(x_2)) / (sum[x_1] - sum[x_2]); 
    }
    // 或者这个
    // LL K(int x_1, int x_2) { 
    //    return (double)(F(x_1) - F(x_2)) / (sum[x_1] - sum[x_2]); 
    //}
    void Delete(int pos) { 
        while(head < tail && 2 * sum[pos] > K(q[head + 1], q[head])) head ++; 
    }
    void Insert(int pos) {
        while(head < tail && K(q[tail], q[tail - 1]) > K(pos, q[tail])) tail --;
        q[++ tail] = pos;
    }
    
    signed main()
    {
        while(cin >> n >> m) { 
            for(int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + read();
            Insert(0);
            for(int i = 1; i <= n; ++i) {
                Delete(i);
                int j = q[head], sum_ = sum[i] - sum[j];
                f[i] = f[j] + sum_ * sum_ + m;
                Insert(i);
            }
            printf("%d\n", f[n]);
            memset(sum, 0, sizeof sum);
            memset(f, 0, sizeof f);
            head = 1, tail = 0;
        }
        return 0;
    }
    
    

    P4360 [CEOI2004]锯木厂选址

    Description

    P4360 [CEOI2004]锯木厂选址

    Solution

    状态设计可能不好想,考虑在哪里建两个厂更省钱

    \(f_i\) 表示将第二个建在 \(i\) 处, 第一个建在 \(j\) 处(\(j < i\))后最多省的钱

    \[\large f_i = sum_i \times dis_i + sum_j \times (dis_j - dis_i) \]

    其中 \(dis_x = \sum_{i = x}^{n} d_i,sum_x = \sum_{i = 1}^{x}w_i, val_x = \sum_{i = 1}^{x} w_i \times dis_i\)

    \(j\)\(k\) 优,

    \[sum_i \times dis_i + sum_j \times (dis_j - dis_i) \ge sum_i \times dis_i + sum_k \times (dis_k - dis_i)\]

    \(F(x) = s_xd_x\) 化简得

    \[d_i \ge \frac{F(k) - F(j)}{s_k - s_j} \]

    都不建的花费为

    \[\sum_{i = 1}^{n} w_i \times dis_i \]

    所以都不建的花费可以表示为 \(val_n\)

    最后的答案为 \(val_n - \max_{1 \le i \le n} \{f_i \}\)

    Code

    /*
    Work by: Suzt_ilymics
    Knowledge: ??
    Time: O(??)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define LL long long
    #define int long long
    #define orz cout<<"lkp AK IOI!"<<endl
    
    using namespace std;
    const int MAXN = 2e5+5;
    const int INF = 1e9+7;
    const int mod = 1e9+7;
    
    int n, ans = 0;
    int w[MAXN], d[MAXN];
    int sum[MAXN], val[MAXN];
    int f[MAXN];
    int q[MAXN], head = 1, tail = 0; 
    
    int read(){
        int s = 0, f = 0;
        char ch = getchar();
        while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
        while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
        return f ? -s : s;
    }
    
    int F(int x) { return sum[x] * d[x] - sum[x] * d[n + 1]; }
    double K(int x_1, int x_2) {
        if(sum[x_1] == sum[x_2]) return F(x_1) > F(x_2) ? 1e9 : -1e9;
        return (double)((F(x_1) - F(x_2)) / (sum[x_1] - sum[x_2])); 
    }
    void Delete(int pos) { while(head < tail && d[pos] < K(q[head], q[head + 1])) head ++; }
    void Insert(int pos) {
        while(head < tail && K(q[tail], q[tail - 1]) < K(q[tail], pos)) tail --;
        q[++ tail] = pos;
    }
    
    signed main()
    {
        n = read();
        for(int i = 1; i <= n; ++i) w[i] = read(), d[i] = read();
        for(int i = n; i >= 1; --i) d[i] += d[i + 1];
        for(int i = 1; i <= n + 1; ++i) sum[i] = sum[i - 1] + w[i], val[i] = val[i - 1] + w[i] * d[i];
        Insert(0);
        for(int i = 1; i <= n; ++i) {
            Delete(i);
            int j = q[head];
            f[i] = sum[i] * d[i] + sum[j] * (d[j] - d[i]);
            Insert(i);
        }
        for(int i = 1; i <= n; ++i) ans = max(ans, f[i]);
        printf("%lld", val[n] - ans);
        return 0;
    }
    
    

    CF311B Cats Transport

    Description

    题面:CF311B Cats Transport

    Solution

    好题哇好题!

    一开始拿到这题,那是半点思路也没有(wtcl

    知道我从讨论中翻到了以为大佬的这张图:

    不得不说,这张图给了我启发

    饲养员的路径是一条形似 \(y = x + b\) 的直线
    而每只猫可以看做一个点,横坐标是它到 \(1\) 号山的距离,总左边是它玩完的时间

    假设一个饲养员可以恰好接走这只猫,根据初中的一次函数代入点的坐标解出 \(b\) 的值(即饲养员恰好接走这只猫的出发时间),先将其存到 \(cat\) 数组中

    再来看一下下面这张图,其中 \(A - F\) 均代表一只猫, \(H, I\) 分别是 \(E, F\) 两个点所在的直线与 \(y\) 轴的交点

    不难发现,假设一个饲养员能够接走 \(D, E, F\),那么他最优时是从 \(10\) 出发,也就是 \(cat_D\),那么 \(E, F\) 两只猫等待的时间分别为 \(cat_D - cat_E, cat_D - cat_F\)

    \(cat\) 数组不是递增的要先排个序

    看起来有点眉目,让我们先yy个转移方程,因为只有 \(p\) 个饲养员,再加一维表示轮到第几个饲养员即可

    \(f_{i, j}\) 为恰好接到第 \(j\) 只猫用 \(i\) 个人时所用的最小等待时间,其中 \(q\) 表示上一只恰好接到的猫为第 \(q\) 只。

    \[f_{i, j} = f_{i - 1, q} + \sum_{k = q + 1}^{j} cat_j - cat_k \]

    \[f_{i, j} = f_{i - 1, q} + (j - q) cat_j - \sum_{k = q + 1}^{j} cat_k \]

    其中 \(t_i\) 表示第 \(i\) 只猫玩完的时间, \(sum_i\) 表示第 \(i\) 只猫到 \(1\) 号山的距离

    \(val_x = \sum_{i = 1}^{x} (t_i - sum_i)\),所以

    \[f_{i, j} = f_{i - 1, q} + (j - q)cat_j + val_j - val_p \]

    假设 \(q\)\(k\)

    \[f_{i - 1, q} + (j - q)cat_j - val_j + val_q \le f_{i - 1, k} + (j - k)cat_j - val_j + val_k \]

    化简得

    \[cat_j \le \frac{(f_{i - 1, k} - val_k) - (f_{i - 1, q} - val_q)}{k - q} \]

    \(F(x) = f_{i - 1, x} - val_x\),则

    \[cat_j \le \frac{F(k) - F(q)}{k - q} \]

    然后进行斜率优化即可

    Code

    /*
    Work by: Suzt_ilymics
    Knowledge: ??
    Time: O(??)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define LL long long
    #define int long long
    #define orz cout<<"lkp AK IOI!"<<endl
    
    using namespace std;
    const int MAXN = 1e5+5;
    const int INF = 1e9+7;
    const int mod = 1e9+7;
    
    int n, m, p;
    int sum[MAXN], val[MAXN], cat[MAXN];
    int f[105][MAXN];
    int q[MAXN], head = 1, tail = 0;
    
    int read(){
        int s = 0, f = 0;
        char ch = getchar();
        while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
        while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
        return f ? -s : s;
    }
    
    int F(int i, int x) { return f[i - 1][x] + val[x]; }
    double K(int i, int x_1, int x_2) { return 1.0 * (F(i, x_1) - F(i, x_2)) / (x_1 - x_2); }
    void Delete(int i, int pos) { while(head < tail && cat[pos] > K(i, q[head], q[head + 1])) head ++; }
    void Insert(int i, int pos) {
        while(head < tail && K(i, q[tail], q[tail - 1]) > K(i, q[tail], pos)) tail --;
        q[++ tail] = pos;
    }
    
    signed main()
    {
        n = read(), m = read(), p = read();
        for(int i = 2; i <= n; ++i) sum[i] = sum[i - 1] + read();
        for(int i = 1, H, T; i <= m; ++i) {
            H = read(), T = read();
            cat[i] = T - sum[H];
        }
        sort(cat + 1, cat + m + 1);
        for(int i = 1; i <= m; ++i) val[i] = val[i - 1] + cat[i];
        for(int i = 1; i <= m; ++i) f[1][i] = i * cat[i] - val[i];
        for(int i = 2; i <= p; ++i) {
            head = 1, tail = 0;
            for(int j = 0; j < i; ++j) Insert(i, j);
            for(int j = i; j <= m; ++j) {
                Delete(i, j);
                int now_ = q[head];
                f[i][j] = f[i - 1][now_] + (j - now_) * cat[j] - val[j] + val[now_];
                Insert(i, j);
            }
        }
        printf("%lld", f[p][m]);
        return 0;
    }
    

    P5504 [JSOI2011] 柠檬

    Description

    题面:P5504 [JSOI2011] 柠檬

    Solution

    暂咕

    Code

    /*
    Work by: Suzt_ilymics
    Knowledge: ??
    Time: O(??)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    #define LL long long
    #define int long long
    #define orz cout<<"lkp AK IOI!"<<endl
    
    using namespace std;
    const int MAXN = 2e5+5;
    const int INF = 1e9+7;
    const int mod = 1e9+7;
    
    int n;
    int a[MAXN], sum[MAXN], head[MAXN];
    vector<int> stc[20010];
    int f[MAXN];
    
    int read(){
        int s = 0, f = 0;
        char ch = getchar();
        while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
        while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
        return f ? -s : s;
    }
    
    int X(int pre_) { return 2 * a[pre_] * sum[pre_]; }
    int F(int pre_) { return f[pre_ - 1] + a[pre_] * sum[pre_] * sum[pre_]; }
    double K(int x_1, int x_2) { return (double)((F(x_1) - F(x_2)) / (X(x_1) - X(x_2))); }
    
    void Delete(int pos) {
        int siz_ = a[pos], top = stc[siz_].size();
        while(top > 1 && K(stc[siz_][top - 1], stc[siz_][top - 2]) < sum[pos] + 1) stc[siz_].pop_back(), top --;
    }
    
    void Insert(int pos) {
        int siz_ = a[pos], top = stc[siz_].size();
        while(top > 1 && K(stc[siz_][top - 1], stc[siz_][top - 2]) < K(stc[siz_][top - 1], pos)) stc[siz_].pop_back(), top --;
        stc[siz_].push_back(pos);
    }
    
    signed main()
    {
        n = read();
        for(int i = 1; i <= n; ++i) {
            a[i] = read();
            sum[i] = sum[head[a[i]]] + 1;
            head[a[i]] = i;
        }
    //    for(int i = 1; i <= n; ++i) cout<<sum[i]<<" ";
    //    cout<<"\n";
        for(int i = 1; i <= n; ++i) {
            Insert(i);
            Delete(i);
            int j = stc[a[i]][stc[a[i]].size() - 1], sum_ = sum[i] - sum[j] + 1;
    //        cout<<j<<" "<<sum_<<" ";
            f[i] = f[j - 1] + a[i] * sum_ * sum_;
    //        cout<<f[i]<<" \n";
        }
        printf("%lld", f[n]);
        return 0;
    }
    
    
  • 相关阅读:
    python模块之xlrd,xlwt,读写execl(xls,xlsx)
    Gulp的常见用法
    Linux常用操作及命令大全
    解决谷歌云 ssh 登录权限被拒的问题 google cloud (publickey,gssapi-keyex,gssapi-with-mic)
    创建web服务器
    网站与服务器的基本知识
    flex布局与移动页面适应
    史上最全的maven的pom.xml文件详解
    linux maven安装
    在 Linux 下搭建 Git 服务器(yum安装)
  • 原文地址:https://www.cnblogs.com/Silymtics/p/14562307.html
Copyright © 2011-2022 走看看