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\n", 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\n", calc(L, R - 1));
    }
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    work();
    return 0;
}
View Code

 

posted on 2016-12-09 23:25  stupid_one  阅读(236)  评论(0编辑  收藏  举报

导航