题目链接 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. BXS入门赛部分writeup

    pwn1  盲打(笑) 前言:没有听鱼哥的话,事先没有装好环境,于是开始没做出来,然后全程在装pwntools,经过一番努力,失败了0.0 最终在网上搜了一段python socket连接脚本,终于可 ...

  2. H3C交换机系统时间设置漏洞

    H3C交换机系统时间设置存在漏洞 1. 背景说明 由于在编写<主机房网络延伸实施方案>,调试H3C S5120S-28P-EI交换机时,发现交换机设置成现在的时间后,导致本地用户通过ssh ...

  3. java面试宝典第三弹

    Http和Https的区别 超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之 ...

  4. Codeforces Round #510 #C Array Product

    http://codeforces.com/contest/1042/problem/C 给你一个有n个元素序列,有两个操作:1,选取a[i]和a[j],删除a[i],将$a[i]*a[j]$赋值给a ...

  5. 文件操作-cd

    cd命令是linux实际使用当中另一个非常重要的命令,本文就为大家介绍下Linux中cd命令的用法. 转载自 https://www.cnblogs.com/waitig/p/5880719.html ...

  6. PAT Basic 1003

    1003 我要通过! “答案正确”是自动判题系统给出的最令人欢喜的回复.本题属于PAT的“答案正确”大派送 —— 只要读入的字符串满足下列条件,系统就输出“答案正确”,否则输出“答案错误”. 得到“答 ...

  7. 误删除innodb ibdata数据文件-之恢复

    今天在群里看到有人说不熟悉innodb把ibdata(数据文件)和ib_logfile(事务日志)文件误删除了.不知道怎么解决.当时我也不知道怎么办.后来查阅相关资料.终找到解决方法.其实恢复也挺简单 ...

  8. 关于springmvc返回json格式数据

    1.引入maven依赖 <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifac ...

  9. Knockout v3.4.0 中文版教程-10-绑定-控制文本内容和外观-visible绑定

    4.绑定 1. 控制文本内容和外观 1. visible绑定 目的 visible绑定可以根据你传入绑定的值控制关联的DOM元素显示或隐藏. 例子 <div data-bind="vi ...

  10. Java-得到类的包

    package com.tj; public class MyClass2 { public static void main(String[] args) { Class cls = java.la ...