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 = + ;
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 = ; i <= n; ++i) {
// cin >> a[i];
scanf("%d", &a[i]);
}
for (int i = ; i <= n - ; ++i) {
dis[i] = abs(a[i + ] - a[i]);
tdis[i] = dis[i];
}
sort(tdis + , tdis + + n - );
dis[n] = inf;
int top = ;
STACK[top].id = ;
STACK[top].val = dis[];
for (int i = ; i <= n; ++i) {
while (top >= && 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[] = inf;
top = ;
STACK[top].id = n - ;
STACK[top].val = dis[n - ];
for (int i = n - ; i >= ; --i) {
while (top >= && dis[i] > STACK[top].val) {
topre[STACK[top].id] = i;
--top;
}
++top;
STACK[top].id = i;
STACK[top].val = dis[i];
} dis[] = inf;
top = ;
STACK[top].val = dis[n - ];
STACK[top].id = n - ;
for (int i = n - ; i >= ; --i) {
while (top >= && 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 = ;
while (q--) {
int L, R;
scanf("%d%d", &L, &R);
LL ans = ;
for (int i = L; i <= R - ; ++i) {
int be = max(L - , topre[i]);
int en = min(R, tonext[i]);
int pos = lower_bound(tdis + , tdis + + n - , dis[i]) - tdis;
if (vis[pos] == cnt) {
be = max(L - , 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 ;
}

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 = + ;
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 = ; i <= n - ; ++i) {
dis[i] = abs(a[i + ] - a[i]);
}
dis[n] = inf;
int top = ;
for (int i = ; i <= n; ++i) {
while (top >= && dis[i] > st[top].val) {
toNext[st[top].id] = i;
--top;
}
++top;
st[top].val = dis[i];
st[top].id = i;
}
top = ;
dis[] = inf;
for (int i = n - ; i >= ; --i) {
while (top >= && 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 = ;
for (int i = be; i <= en; ++i) {
int L = max(be, toPre[i] + );
int R = min(en, toNext[i] - );
ans += 1LL * dis[i] * (i - L + ) * (R - i + );
}
return ans;
}
void work() {
scanf("%d%d", &n, &q);
for (int i = ; 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 - ));
}
} int main() {
#ifdef local
freopen("data.txt", "r", stdin);
// freopen("data.txt", "w", stdout);
#endif
work();
return ;
}

CF 602 D. Lipshitz Sequence 数学 + 单调栈 + 优化的更多相关文章

  1. Codeforces 601B. Lipshitz Sequence(单调栈)

    Codeforces 601B. Lipshitz Sequence 题意:,q个询问,每次询问给出l,r,求a数组[l,r]中所有子区间的L值的和. 思路:首先要观察到,斜率最大值只会出现在相邻两点 ...

  2. [CF442C] Artem and Array (贪心+单调栈优化)

    题目链接:http://codeforces.com/problemset/problem/442/C 题目大意:一个数列,有n个元素.你可以做n-2次操作,每次操作去除一个数字,并且得到这个数字两边 ...

  3. 【P2422】良好的感觉(单调栈优化DP//奇怪的暴力)

    话说正解是单调栈优化DP,然而貌似根据某种玄学的推算,这个题暴力出解貌似也是可以的.首先,我们枚举所有的点作为最小点,然后横向展开,遇到更小的就停止...然后再操作一下,看上去时间O(N^2),然而由 ...

  4. csp-s模拟测试50(9.22)「施工(单调栈优化DP)」·「蔬菜(二维莫队???)」·「联盟(树上直径)」

    改了两天,终于将T1,T3毒瘤题改完了... T1 施工(单调栈优化DP) 考场上只想到了n*hmaxn*hmaxn的DP,用线段树优化一下变成n*hmaxn*log但显然不是正解 正解是很**的单调 ...

  5. 【BZOJ1345】[Baltic2007]序列问题Sequence 贪心+单调栈

    [BZOJ1345][Baltic2007]序列问题Sequence Description 对于一个给定的序列a1, …, an,我们对它进行一个操作reduce(i),该操作将数列中的元素ai和a ...

  6. 2019年杭电多校第二场 1002题Beauty Of Unimodal Sequence(LIS+单调栈)

    题目链接 传送门 思路 首先我们对\(a\)正反各跑一边\(LIS\),记录每个位置在前一半的\(LIS\)中应该放的位置\(ans1[i]\),后一半的位置\(ans2[i]\). 对于字典序最小的 ...

  7. [poj3017] Cut the Sequence (DP + 单调队列优化 + 平衡树优化)

    DP + 单调队列优化 + 平衡树 好题 Description Given an integer sequence { an } of length N, you are to cut the se ...

  8. poj3017 Cut the Sequence[平衡树+单调队列优化]

    这里已经讲得很清楚了. 本質上是決策點與區間最大值有一定關係,於是用单调队列来维护决策集合(而不是常规的),然后在决策集合中选取最小值. 然后觉得这题方法还是很重要的.没写平衡树,用优先队列(堆)来维 ...

  9. POJ - 3415 Common Substrings(后缀数组求长度不小于 k 的公共子串的个数+单调栈优化)

    Description A substring of a string T is defined as: T( i, k)= TiTi+1... Ti+k-1, 1≤ i≤ i+k-1≤| T|. G ...

随机推荐

  1. ssh免密码访问

    ssh-copy-id命令 它可以把本地主机的公钥复制到远程主机的authorized_keys文件上,ssh-copy-id命令也会给远程主机的用户主目录(home)和~/.ssh, 和~/.ssh ...

  2. 在无代码文件的aspx文件中添加类、函数和字段的方法

    大家都知道,在开始WebForm程序时,一个WebForm由.cs代码文件与.aspx页面文件组成.在aspx文件中可以嵌入C#代码,但无法在aspx的嵌入C#代码中定义类,函数和字段等.这样,就限制 ...

  3. bootstrap中的less

    一.如何加入变量 引入你的 .less 样式文件的时候要设置 rel 属性值为 “stylesheet/less”: 参考网站:http://www.bootcss.com/p/lesscss/  1 ...

  4. Android系统设置Android adb 开关的方法【转】

    本文转载自:http://www.wxtlife.com/2015/11/24/Android-set-adb-status/ 想第一时间获取我的最新文章,请关注公众号: 技术特工队 在整机系统开发中 ...

  5. svn服务器搭建与迁移

    2016-11-21更新: 今天被svn的钩子搞了半天,网上找解决方法都无效,下午被我试出来了,特此记录. 在svn的钩子中可以使用update来更新配置文件,比如ansible的,puppet的,具 ...

  6. java中的泛型类和泛型方法

    1.泛型是什么? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类. 可以在集合框架(Collection fram ...

  7. hihocoder-1347 小h的树上的朋友(lca+线段树)

    题目链接: 小h的树上的朋友 时间限制:18000ms 单点时限:2000ms 内存限制:512MB 描述 小h拥有n位朋友.每位朋友拥有一个数值Vi代表他与小h的亲密度.亲密度有可能发生变化. 岁月 ...

  8. I.MX6 MAC Address hacking

    /************************************************************************** * I.MX6 MAC Address hack ...

  9. 并不对劲的bzoj1758:p4292:[WC2010]重建计划

    题目大意 \(n\)(\(n\leq10^5\))个点的一棵树,有边权\(w\),给定\(l,r\),求边数在\([l,r]\)中的路径的平均边权的最大值 题解 二分答案,判断时将边权变成\(w-mi ...

  10. SPOJ:House Fence(分治&DP)

    "Holiday is coming, holiday is coming, hurray hurray!" shouts Joke in the last day of his ...