题目链接 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)$

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define lson i << 1, L, mid
#define rson i << 1 | 1, mid + 1, R typedef long long LL; const int N = 1e5 + 10;
const int A = 18; int f[N][A], g[N][A];
int a[N], lg[N], c[N], dp[N];
int n, s, l;
int L, R; int t[N << 2]; void ST(){
rep(i, 1, n) f[i][0] = a[i];
rep(j, 1, 17) rep(i, 1, n)
if ((i + (1 << j) - 1) <= n) f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); rep(i, 1, n) g[i][0] = a[i];
rep(j, 1, 17) rep(i, 1, n)
if ((i + (1 << j) - 1) <= n) g[i][j] = max(g[i][j - 1], g[i + (1 << (j - 1))][j - 1]); } inline int solvemin(int l, int r){
int k = lg[r - l + 1];
return min(f[l][k], f[r - (1 << k) + 1][k]);
} inline int solvemax(int l, int r){
int k = lg[r - l + 1];
return max(g[l][k], g[r - (1 << k) + 1][k]);
} inline void pushup(int i){ t[i] = min(t[i << 1], t[i << 1 | 1]);} void build(int i, int L, int R){
if (L == R){ t[i] = 1 << 30; return;}
int mid = (L + R) >> 1;
build(lson);
build(rson);
pushup(i);
} void update(int i, int L, int R, int x, int val){
if (L == R && L == x){ t[i] = min(t[i], val); return;}
int mid = (L + R) >> 1;
if (x <= mid) update(lson, x, val);
else update(rson, x, val);
pushup(i);
} int query(int i, int L, int R, int l, int r){
if (L == l && R == r) return t[i];
int mid = (L + R) >> 1;
if (r <= mid) return query(lson, l, r);
else if (l > mid) return query(rson, l, r);
else return min(query(lson, l, mid), query(rson, mid + 1, r));
} int main(){ rep(i, 1, 1e5 + 1) lg[i] = (int)log2((double)(i)); scanf("%d%d%d", &n, &s, &l);
rep(i, 1, n) scanf("%d", a + i); ST(); if (l <= 1) c[1] = 1; else c[1] = -1;
rep(i, 2, n){
L = 1, R = i - l + 1;
if (R < 1){ c[i] = -1; continue; }
if (solvemax(R, i) - solvemin(R, i) > s){ c[i] = -1; continue;}
while (L + 1 < R){
int mid = (L + R) >> 1;
if (solvemax(mid, i) - solvemin(mid, i) <= s) R = mid;
else L = mid + 1;
} if (solvemax(L, i) - solvemin(L, i) <= s) c[i] = L;
else c[i] = R;
} dp[0] = 0;
rep(i, 1, n) dp[i] = 1 << 30; build(1, 1, n + 1);
update(1, 1, n + 1, 1, 0); rep(i, 1, n){
if (c[i] == -1) continue;
int now = query(1, 1, n + 1, c[i], i - l + 1);
dp[i] = min(dp[i], now + 1);
update(1, 1, n + 1, i + 1, dp[i]);
} if (dp[n] < (1 << 30)) printf("%d\n", dp[n]);
else puts("-1");
return 0;
}

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

其实有一种更好的方法。

用单调队列来优化。

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

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

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

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

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i) const int N = 1e5 + 10; int n, s, l, now;
int a[N], f[N];
multiset <int> st, v; int main(){ scanf("%d%d%d", &n, &s, &l);
rep(i, 1, n) scanf("%d", a + i); now = 1;
f[0] = 0;
rep(i, 1, n){
f[i] = 1 << 30;
st.insert(a[i]);
if (i - now + 1 >= l) v.insert(f[i - l]);
while (*st.rbegin() - *st.begin() > s){
st.erase(st.find(a[now]));
auto it = v.find(f[now - 1]);
if (it != v.end()) v.erase(it);
// if (i - now + 1 >= l) v.erase(v.find(f[now - 1]));
++now;
}
if (!v.empty()) f[i] = *v.begin() + 1;
} printf("%d\n", f[n] >= (1 << 30) ? -1 : f[n]);
return 0;
}

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. intellij IDEA版本控制设置

    我们开发肯定是有版本控制的,大家以前Eclipse的时候在本地文件和版本库不一致的时候,那么文件以及所在的文件夹都会出现一个〉表示,大家能很轻松的看到本地文件修改了哪一些,但是IntelliJ中默认是 ...

  2. shell脚本,如何写进度条。

    [root@localhost ~]# cat jindutiao.sh #!/bin/bash #进度条 n=$((/)) N=$((/)) ` do sleep 0.01 [ $(($i%$n)) ...

  3. 【树状数组 离散化】bzoj1573: [Usaco2009 Open]牛绣花cowemb

    解方程题! Description Bessie学会了刺绣这种精细的工作.牛们在一片半径为d(1 <= d <= 50000)的圆形布上绣花. 它们一共绣了N (2 <= N < ...

  4. [LUOGU] P1828 香甜的黄油 Sweet Butter

    题目描述 农夫John发现做出全威斯康辛州最甜的黄油的方法:糖.把糖放在一片牧场上,他知道N(1<=N<=500)只奶牛会过来舔它,这样就能做出能卖好价钱的超甜黄油.当然,他将付出额外的费 ...

  5. 记服务器 httpd 服务无法启动

    httpd 服务无法重启 ,网上查到的资料都是端口号冲突 ,记录一下自己的解决办法 !!! 首先查看 服务状态 :  service status httpd / systemctl status h ...

  6. centos7.2安装redis与配置(史上最全)

    学习了php已经快三年了,一直是在盲目的忙,也没整理下笔记,今天整理一下 分享下安装redis的方法 #首先去redis官网去下载   http://www.redis.cn/download.htm ...

  7. 【xdebug】 windows xdebug 配置

    [xdebug] zend_extension = C:\phpStudy\php53n\ext\php_xdebug-2.6.1-7.0-vc14-nts-x86_64.dllxdebug.idek ...

  8. python--前端CSS

    一.CSS介绍 CSS(Cascading Style Sheet,层叠样式表)定义了如何显示HTML元素,给HTML设置样式,让他更加美观. 当浏览器读到这个样式表, 他就会按照这个样式来对文档进行 ...

  9. linux时钟概念CST与UTC、以及NTP简单设置

    1.世界协调时间(Universal Time Coordinated,UTC): GPS 系统中有两种时间区分,一为UTC,另一为LT(地方时)两者的区别为时区不同,UTC就是0时区的时间,地方时为 ...

  10. PAT Basic 1035

    1035 插入与归并 根据维基百科的定义: 插入排序是迭代算法,逐一获得输入数据,逐步产生有序的输出序列.每步迭代中,算法从输入序列中取出一元素,将之插入有序序列中正确的位置.如此迭代直到全部元素有序 ...