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. 分析PHP的include机制

    php在解析include指令时,会对包含的文件路径做如下判断: 如果是绝对路径,则直接包含,并结束. 如果是相对路径,则做如下判断: 相对路径以特殊符号开头,如 "./1.php" ...

  2. 织梦DEDE后台定时分时段自动更新发布文章插件

    定时审核插件使用说明 一.立信CPA培训注册会计师考试网站 以超级管理员身份登录后台,依次选择[核心]à [定时审核管理],输入定时审核的时间段,如下图所示: 功能说明: 1. 可以设置若干时间段,在 ...

  3. receive和process的过程

    (一) receive最终在fuse_kern_chan.c中的fuse_kern_chan_receive函数实现,使用系统调用读取 res = read(fuse_chan_fd(ch), buf ...

  4. 安装程序工具 (Installutil.exe)22

    网址:https://msdn.microsoft.com/zh-cn/library/50614e95(VS.80).aspx  安装程序工具 (Installutil.exe) .NET Fram ...

  5. 【原】WPF客户端只能启动一次

    public partial class App : Application { System.Threading.Mutex mutex; public App() { this.Startup + ...

  6. 隐马尔科夫模型HMM

    崔晓源 翻译 我们通常都习惯寻找一个事物在一段时间里的变化规律.在很多领域我们都希望找到这个规律,比如计算机中的指令顺序,句子中的词顺序和语音中的词顺序等等.一个最适用的例子就是天气的预测. 首先,本 ...

  7. 90年代经典“手游”—拼图板小游戏Opencv实现

    80后可能还对儿时玩过的一种经典木质的拼图板游戏记忆犹新,一般是一种4*4或5*5规格的手持活动板,通过挪动每个小板子的位置,拼出来板子上完整的图像,那时候还没有网吧,手机也还是大哥大的天下,所以这也 ...

  8. CodeForces937B:Vile Grasshoppers(素数性质)

    The weather is fine today and hence it's high time to climb the nearby pine and enjoy the landscape. ...

  9. UI:地图和定位

    参考学习链接 各种IOS设备可以使用 Core Location 框架确定它的物理位置.core location 主要使用三种技术来实现功能.GPS.蜂窝基站三角网络定位. wifi 定位服务.这三 ...

  10. HDU 1996 汉诺塔VI (排列组合)

    题意:... 析:每次都是有三种放法,1,2,3,根柱子,所以就是3^n次方. 代码如下: #pragma comment(linker, "/STACK:1024000000,102400 ...