zoukankan      html  css  js  c++  java
  • CF 602 D. Lipshitz Sequence 数学 + 单调栈 + 优化

    http://codeforces.com/contest/602/problem/D

    这题需要注意到的是,对于三个点(x1, y1)和(x2, y2)和(x3, y3)。如果要算出区间[1, 3]的L(h)函数的最大值,则一定不会是

    通过(y3 - y1) / (x3 - x1)算出。因为很简单,通过(x2, y2)作为辅助点,数值将会更加大。

    然后设dis[i]表示abs(a[i + 1] - a[i])。那么区间[L, R]的最大值就是在dis[]中,[L, R - 1]数值的最大值。

    因为,不断套用上面那个结论,先从两个点的时候出发,a[2] - a[1]就是最大,然后,如果三个点,那么可能是a[3] - a[2]和a[2] - a[1]中的较大者。4个点的时候同理,每次只需要用前一个的最大值和a[new] - a[new - 1]比较,取最大的即可。

    比如样例

    a[]:  1、5、2、9、1、3、4、2、1、7

    dis[]:  4、3、7、8、2、1、2、1、6

    但是还是要枚举每个子区间,那么复杂度还是不变。

    那么要把问题转化下,转化成求以dis[i]为最大值的区间数有多少个。

    那么可以用单调栈维护出tonext[i]表示右边第一个大于dis[i]的数。topre[i]表示左边第一个大于dis[i]的数。

    注意判断不要超过[L, R]的范围。

    然后两个数值相乘,就是dis[i]为最大值的区间数。

    但是有点bug。比如上面的 2、1、2、1

    算了第一个2,那么后面的2就不应该重复计算。那么需要标记是否vis[]

    如果vis[],那么需要找到第一个等于2的数就行了,所以我用了三次单调栈。

    感觉有点复杂。

    但是真正自己想到了的话,写代码是很愉快的。

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <assert.h>
    #define IOS ios::sync_with_stdio(false)
    using namespace std;
    #define inf (0x3f3f3f3f)
    typedef long long int LL;
    
    
    #include <iostream>
    #include <sstream>
    #include <vector>
    #include <set>
    #include <map>
    #include <queue>
    #include <string>
    const int maxn = 100000 + 20;
    int a[maxn];
    int dis[maxn];
    int tonext[maxn];
    int topre[maxn];
    int topresec[maxn];
    int vis[maxn];
    int tdis[maxn];
    struct node {
        int id;
        int val;
    }STACK[maxn];
    void work() {
        int n, q;
    //    IOS;
    //    cin >> n >> q;
        scanf("%d%d", &n, &q);
        for (int i = 1; i <= n; ++i) {
    //        cin >> a[i];
            scanf("%d", &a[i]);
        }
        for (int i = 1; i <= n - 1; ++i) {
            dis[i] = abs(a[i + 1] - a[i]);
            tdis[i] = dis[i];
        }
        sort(tdis + 1, tdis + 1 + n - 1);
        dis[n] = inf;
        int top = 1;
        STACK[top].id = 1;
        STACK[top].val = dis[1];
        for (int i = 2; i <= n; ++i) {
            while (top >= 1 && dis[i] > STACK[top].val) {
                tonext[STACK[top].id] = i;
                top--;
            }
            ++top;
            STACK[top].id = i;
            STACK[top].val = dis[i];
        }
    //    for (int i = 1; i <= n; ++i) {
    //        printf("%d ", tonext[i]);
    //    }
        dis[0] = inf;
        top = 1;
        STACK[top].id = n - 1;
        STACK[top].val = dis[n - 1];
        for (int i = n - 2; i >= 0; --i) {
            while (top >= 1 && dis[i] > STACK[top].val) {
                topre[STACK[top].id] = i;
                --top;
            }
            ++top;
            STACK[top].id = i;
            STACK[top].val = dis[i];
        }
    
        dis[0] = inf;
        top = 1;
        STACK[top].val = dis[n - 1];
        STACK[top].id = n - 1;
        for (int i = n - 2; i >= 0; --i) {
            while (top >= 1 && dis[i] >= STACK[top].val) {
                topresec[STACK[top].id] = i;
                --top;
            }
            ++top;
            STACK[top].id = i;
            STACK[top].val = dis[i];
        }
    //    for (int i = 1; i <= n; ++i) {
    //        printf("%d ", topresec[i]);
    //    }
        int cnt = 1;
        while (q--) {
            int L, R;
            scanf("%d%d", &L, &R);
            LL ans = 0;
            for (int i = L; i <= R - 1; ++i) {
                int be = max(L - 1, topre[i]);
                int en = min(R, tonext[i]);
                int pos = lower_bound(tdis + 1, tdis + 1 + n - 1, dis[i]) - tdis;
                if (vis[pos] == cnt) {
                    be = max(L - 1, topresec[i]);
                }
                vis[pos] = cnt;
                ans += (LL)dis[i] * (i - be) * (en - i);
            }
            cnt++;
            printf("%I64d
    ", ans);
        }
    }
    
    int main() {
    #ifdef local
        freopen("data.txt", "r", stdin);
    //    freopen("data.txt", "w", stdout);
    #endif
        work();
        return 0;
    }
    View Code

    2017年3月9日 17:10:38

    现在 回顾起这题,当时的写法确实有点复杂,其实要解决那个bug,只需要维护toNext[i]表示大于dis[i]这个数字的第一个位置。toPre[i]表示不小于dis[i]这个数字的位置即可。

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <assert.h>
    #define IOS ios::sync_with_stdio(false)
    using namespace std;
    #define inf (0x3f3f3f3f)
    typedef long long int LL;
    
    
    #include <iostream>
    #include <sstream>
    #include <vector>
    #include <set>
    #include <map>
    #include <queue>
    #include <string>
    #include <bitset>
    const int maxn = 100000 + 20;
    int a[maxn];
    int dis[maxn];
    int n, q;
    struct Stack {
        int val, id;
    }st[maxn];
    int toNext[maxn], toPre[maxn];
    void init() {
        for (int i = 1; i <= n - 1; ++i) {
            dis[i] = abs(a[i + 1] - a[i]);
        }
        dis[n] = inf;
        int top = 0;
        for (int i = 1; i <= n; ++i) {
            while (top >= 1 && dis[i] > st[top].val) {
                toNext[st[top].id] = i;
                --top;
            }
            ++top;
            st[top].val = dis[i];
            st[top].id = i;
        }
        top = 0;
        dis[0] = inf;
        for (int i = n - 1; i >= 0; --i) {
            while (top >= 1 && dis[i] >= st[top].val) {
                toPre[st[top].id] = i;
                --top;
            }
            ++top;
            st[top].val = dis[i];
            st[top].id = i;
        }
    }
    LL calc(int be, int en) {
        LL ans = 0;
        for (int i = be; i <= en; ++i) {
            int L = max(be, toPre[i] + 1);
            int R = min(en, toNext[i] - 1);
            ans += 1LL * dis[i] * (i - L + 1) * (R - i + 1);
        }
        return ans;
    }
    void work() {
        scanf("%d%d", &n, &q);
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
        }
        init();
    //    for (int i = 1; i <= n - 1; ++i) {
    //        cout << toNext[i] << " ";
    //    }
    //    for (int i = 1; i <= n - 1; ++i) {
    //        cout << toPre[i] << " ";
    //    }
    //    for (int i = 1; i <= n - 1; ++i) {
    //        cout << dis[i] << " ";
    //    }
        while (q--) {
            int L, R;
            scanf("%d%d", &L, &R);
            printf("%I64d
    ", calc(L, R - 1));
        }
    }
    
    int main() {
    #ifdef local
        freopen("data.txt", "r", stdin);
    //    freopen("data.txt", "w", stdout);
    #endif
        work();
        return 0;
    }
    View Code
  • 相关阅读:
    JS中的this
    函数作用域
    全局作用域与变量的声明提前
    立即执行函数与For. . .in语句
    实参可以是任何值
    函数的参数和返回值
    JavaScript创建函数的三种方式
    JavaScript属性名和属性值
    十进制浮点数转换为二进制
    冰雹猜想或角谷定理
  • 原文地址:https://www.cnblogs.com/liuweimingcprogram/p/6152541.html
Copyright © 2011-2022 走看看