题目链接 Strip

题意   把一个数列分成连续的$k$段,要求满足每一段内的元素最大值和最小值的差值不超过$s$,

同时每一段内的元素个数要大于等于$l$,

求$k$的最小值。

考虑$DP$

设$dp[i]$为前$i$个数字能划分成区间个数的最小值。

则$dp[i] = min(dp[j] + 1)$

于是下一步就是求符合条件的j的范围。

构建$ST$表,支持区间查询最大值和最小值。

对于每一个位置$x$,我们知道$max(a[i]...a[x]) - min(a[i]...a[x])$肯定是随着i的减小非递减的。$(i <= x)$

于是我们就可以通过二分求出符合条件的$max(a[i]...a[x]) - min(a[i]...a[x])$的最小值,记为$c[x]$

当不存在可以转移到$dp[x]$的$dp[i]$时,$c[x]$为$-1$。

$n <= 100000$,考虑用线段树优化。

单点更新,区间查询最小值。

时间复杂度$O(nlogn)$

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std;
  4.  
  5. #define rep(i, a, b) for (int i(a); i <= (b); ++i)
  6. #define dec(i, a, b) for (int i(a); i >= (b); --i)
  7. #define lson i << 1, L, mid
  8. #define rson i << 1 | 1, mid + 1, R
  9.  
  10. typedef long long LL;
  11.  
  12. const int N = 1e5 + 10;
  13. const int A = 18;
  14.  
  15. int f[N][A], g[N][A];
  16. int a[N], lg[N], c[N], dp[N];
  17. int n, s, l;
  18. int L, R;
  19.  
  20. int t[N << 2];
  21.  
  22. void ST(){
  23. rep(i, 1, n) f[i][0] = a[i];
  24. rep(j, 1, 17) rep(i, 1, n)
  25. if ((i + (1 << j) - 1) <= n) f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
  26.  
  27. rep(i, 1, n) g[i][0] = a[i];
  28. rep(j, 1, 17) rep(i, 1, n)
  29. if ((i + (1 << j) - 1) <= n) g[i][j] = max(g[i][j - 1], g[i + (1 << (j - 1))][j - 1]);
  30.  
  31. }
  32.  
  33. inline int solvemin(int l, int r){
  34. int k = lg[r - l + 1];
  35. return min(f[l][k], f[r - (1 << k) + 1][k]);
  36. }
  37.  
  38. inline int solvemax(int l, int r){
  39. int k = lg[r - l + 1];
  40. return max(g[l][k], g[r - (1 << k) + 1][k]);
  41. }
  42.  
  43. inline void pushup(int i){ t[i] = min(t[i << 1], t[i << 1 | 1]);}
  44.  
  45. void build(int i, int L, int R){
  46. if (L == R){ t[i] = 1 << 30; return;}
  47. int mid = (L + R) >> 1;
  48. build(lson);
  49. build(rson);
  50. pushup(i);
  51. }
  52.  
  53. void update(int i, int L, int R, int x, int val){
  54. if (L == R && L == x){ t[i] = min(t[i], val); return;}
  55. int mid = (L + R) >> 1;
  56. if (x <= mid) update(lson, x, val);
  57. else update(rson, x, val);
  58. pushup(i);
  59. }
  60.  
  61. int query(int i, int L, int R, int l, int r){
  62. if (L == l && R == r) return t[i];
  63. int mid = (L + R) >> 1;
  64. if (r <= mid) return query(lson, l, r);
  65. else if (l > mid) return query(rson, l, r);
  66. else return min(query(lson, l, mid), query(rson, mid + 1, r));
  67. }
  68.  
  69. int main(){
  70.  
  71. rep(i, 1, 1e5 + 1) lg[i] = (int)log2((double)(i));
  72.  
  73. scanf("%d%d%d", &n, &s, &l);
  74. rep(i, 1, n) scanf("%d", a + i);
  75.  
  76. ST();
  77.  
  78. if (l <= 1) c[1] = 1; else c[1] = -1;
  79. rep(i, 2, n){
  80. L = 1, R = i - l + 1;
  81. if (R < 1){ c[i] = -1; continue; }
  82. if (solvemax(R, i) - solvemin(R, i) > s){ c[i] = -1; continue;}
  83. while (L + 1 < R){
  84. int mid = (L + R) >> 1;
  85. if (solvemax(mid, i) - solvemin(mid, i) <= s) R = mid;
  86. else L = mid + 1;
  87. }
  88.  
  89. if (solvemax(L, i) - solvemin(L, i) <= s) c[i] = L;
  90. else c[i] = R;
  91. }
  92.  
  93. dp[0] = 0;
  94. rep(i, 1, n) dp[i] = 1 << 30;
  95.  
  96. build(1, 1, n + 1);
  97. update(1, 1, n + 1, 1, 0);
  98.  
  99. rep(i, 1, n){
  100. if (c[i] == -1) continue;
  101. int now = query(1, 1, n + 1, c[i], i - l + 1);
  102. dp[i] = min(dp[i], now + 1);
  103. update(1, 1, n + 1, i + 1, dp[i]);
  104. }
  105.  
  106. if (dp[n] < (1 << 30)) printf("%d\n", dp[n]);
  107. else puts("-1");
  108. return 0;
  109. }

上面这个方法很容易想,但是写起来略有难度。

其实有一种更好的方法。

用单调队列来优化。

对于当前的这个$x$,向后扫描,扫到y的时候如果发现区间$[x, y]$不符合题意了。

那么其实这个时候$x$这个位置已经没用了。

因为如果$[x,y]$不符合题意,那么$[x, y + 1]$肯定是不符合题意的。

于是我们可以用单调队列优化,用集合来维护最大值和最小值。

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std;
  4.  
  5. #define rep(i, a, b) for (int i(a); i <= (b); ++i)
  6. #define dec(i, a, b) for (int i(a); i >= (b); --i)
  7.  
  8. const int N = 1e5 + 10;
  9.  
  10. int n, s, l, now;
  11. int a[N], f[N];
  12. multiset <int> st, v;
  13.  
  14. int main(){
  15.  
  16. scanf("%d%d%d", &n, &s, &l);
  17. rep(i, 1, n) scanf("%d", a + i);
  18.  
  19. now = 1;
  20. f[0] = 0;
  21. rep(i, 1, n){
  22. f[i] = 1 << 30;
  23. st.insert(a[i]);
  24. if (i - now + 1 >= l) v.insert(f[i - l]);
  25. while (*st.rbegin() - *st.begin() > s){
  26. st.erase(st.find(a[now]));
  27. auto it = v.find(f[now - 1]);
  28. if (it != v.end()) v.erase(it);
  29. // if (i - now + 1 >= l) v.erase(v.find(f[now - 1]));
  30. ++now;
  31. }
  32. if (!v.empty()) f[i] = *v.begin() + 1;
  33. }
  34.  
  35. printf("%d\n", f[n] >= (1 << 30) ? -1 : f[n]);
  36. return 0;
  37. }

Codeforces 487B Strip (ST表+线段树维护DP 或 单调队列优化DP)的更多相关文章

  1. 51nod 1766 树上的最远点对 | LCA ST表 线段树 树的直径

    51nod 1766 树上的最远点对 | LCA ST表 线段树 树的直径 题面 n个点被n-1条边连接成了一颗树,给出a~b和c~d两个区间,表示点的标号请你求出两个区间内各选一点之间的最大距离,即 ...

  2. codeforces Good bye 2016 E 线段树维护dp区间合并

    codeforces Good bye 2016 E 线段树维护dp区间合并 题目大意:给你一个字符串,范围为‘0’~'9',定义一个ugly的串,即串中的子串不能有2016,但是一定要有2017,问 ...

  3. P4381 [IOI2008]Island(基环树+单调队列优化dp)

    P4381 [IOI2008]Island 题意:求图中所有基环树的直径和 我们对每棵基环树分别计算答案. 首先我们先bfs找环(dfs易爆栈) 蓝后我们处理直径 直径不在环上,就在环上某点的子树上 ...

  4. bzoj 1699: [Usaco2007 Jan]Balanced Lineup排队【st表||线段树】

    要求区间取min和max,可以用st表或线段树维护 st表 #include<iostream> #include<cstdio> using namespace std; c ...

  5. Codeforces GYM 100114 D. Selection 线段树维护DP

    D. Selection Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100114 Descriptio ...

  6. [Codeforces]817F. MEX Queries 离散化+线段树维护

    [Codeforces]817F. MEX Queries You are given a set of integer numbers, initially it is empty. You sho ...

  7. BZOJ 3672[NOI2014]购票(树链剖分+线段树维护凸包+斜率优化) + BZOJ 2402 陶陶的难题II (树链剖分+线段树维护凸包+分数规划+斜率优化)

    前言 刚开始看着两道题感觉头皮发麻,后来看看题解,发现挺好理解,只是代码有点长. BZOJ 3672[NOI2014]购票 中文题面,题意略: BZOJ 3672[NOI2014]购票 设f(i)f( ...

  8. Codeforces 1304F1/F2 Animal Observation(单调队列优化 dp)

    easy 题目链接 & hard 题目链接 给出一张 \(n \times m\) 的矩阵,每个格子上面有一个数,你要在每行选出一个点 \((i,t)\),并覆盖左上角为 \((i,t)\), ...

  9. BZOJ1791 [Ioi2008]Island 岛屿[基环树+单调队列优化DP]

    基环树直径裸题. 首先基环树直径只可能有两种形式:每棵基环树中的环上挂着的树的直径,或者是挂在环上的两个树的最大深度根之间的距离之和. 所以,先对每个连通块跑一遍,把环上的点找出来,然后对环上每个点跑 ...

随机推荐

  1. jQuery和CSS的拍摄效果

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. Philipp Wagner

    本文大部分来自OpenCV官网上的Face Reconition with OpenCV这节内容(http://docs.opencv.org/modules/contrib/doc/facerec/ ...

  3. Spring根据XML配置文件注入属性 其实也是造bean,看看是使用constructor还是setter顺带完成属性赋值

    方法一使用setter方法 package com.swift; public class Book { private String bookName; public void setBook(St ...

  4. ios之UIWebView(2)

    UIWebView是iOS sdk中一个最常用的控件.是内置的浏览器控件,我们可以用它来浏览网页.打开文档等等.这篇文章我将使用这个控件,做一个简易的浏览器.如下图: 我们创建一个Window-bas ...

  5. 洛谷 P1663 山

    https://www.luogu.org/problemnew/show/P1663 可能在这里看会好一点:[题解]

  6. C++模板 · 为什么要引入模板机制?

    刚学过类模板时,很不理解,甚至觉得这简直没有用,在自己骗自己嘛!明明很方便的东西,偏偏要加个类模板来回折腾.可能因为我们刚开始写的程序很简单,有时候,可能程序复杂一点,对理解一些概念更有帮助. 今天在 ...

  7. (34)zabbix Queue队列

    概述 queue(队列)显示监控项等待刷新的时间,可以看到每种agent类型刷新时间,通过queue可以更好的体现出监控的一个指标.正常情况下,是一片绿色. 如果出现过多红色,那么需要留意一下.我们也 ...

  8. (20)zabbix触发器triggers

    触发器是什么 触发器(triggers)是什么?触发器使用逻辑表达式来评估通过item获取到得数据是处于哪种状态,item一收回数据,讲解任务交给触发器去评估状态,明白触发器是怎么一回事了把?在触发器 ...

  9. 微信小程序 wx.request POST请求------中文乱码问题

    问题: 一个简单的表单,提交后台返回数据“提交成功”. 以为没问题了,但是没过多久后台小哥就问为啥那么多乱码,找了很久原因,发现在提交的时候就已经乱码了. 嗯,前端问题,然后测试GET/POST方法. ...

  10. LeetCode(8)String to Integer (atoi)

    题目: Implement atoi to convert a string to an integer. Hint: Carefully consider all possible input ca ...