您的位置:首页 > 产品设计 > UI/UE

CF 602 D. Lipshitz Sequence 数学 + 单调栈 + 优化

2016-12-09 23:25 323 查看
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>
#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
= 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: