Codeforces 487B Strip (ST表+线段树维护DP 或 单调队列优化DP)
题目链接 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)的更多相关文章
- 51nod 1766 树上的最远点对 | LCA ST表 线段树 树的直径
51nod 1766 树上的最远点对 | LCA ST表 线段树 树的直径 题面 n个点被n-1条边连接成了一颗树,给出a~b和c~d两个区间,表示点的标号请你求出两个区间内各选一点之间的最大距离,即 ...
- codeforces Good bye 2016 E 线段树维护dp区间合并
codeforces Good bye 2016 E 线段树维护dp区间合并 题目大意:给你一个字符串,范围为‘0’~'9',定义一个ugly的串,即串中的子串不能有2016,但是一定要有2017,问 ...
- P4381 [IOI2008]Island(基环树+单调队列优化dp)
P4381 [IOI2008]Island 题意:求图中所有基环树的直径和 我们对每棵基环树分别计算答案. 首先我们先bfs找环(dfs易爆栈) 蓝后我们处理直径 直径不在环上,就在环上某点的子树上 ...
- bzoj 1699: [Usaco2007 Jan]Balanced Lineup排队【st表||线段树】
要求区间取min和max,可以用st表或线段树维护 st表 #include<iostream> #include<cstdio> using namespace std; c ...
- Codeforces GYM 100114 D. Selection 线段树维护DP
D. Selection Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100114 Descriptio ...
- [Codeforces]817F. MEX Queries 离散化+线段树维护
[Codeforces]817F. MEX Queries You are given a set of integer numbers, initially it is empty. You sho ...
- BZOJ 3672[NOI2014]购票(树链剖分+线段树维护凸包+斜率优化) + BZOJ 2402 陶陶的难题II (树链剖分+线段树维护凸包+分数规划+斜率优化)
前言 刚开始看着两道题感觉头皮发麻,后来看看题解,发现挺好理解,只是代码有点长. BZOJ 3672[NOI2014]购票 中文题面,题意略: BZOJ 3672[NOI2014]购票 设f(i)f( ...
- Codeforces 1304F1/F2 Animal Observation(单调队列优化 dp)
easy 题目链接 & hard 题目链接 给出一张 \(n \times m\) 的矩阵,每个格子上面有一个数,你要在每行选出一个点 \((i,t)\),并覆盖左上角为 \((i,t)\), ...
- BZOJ1791 [Ioi2008]Island 岛屿[基环树+单调队列优化DP]
基环树直径裸题. 首先基环树直径只可能有两种形式:每棵基环树中的环上挂着的树的直径,或者是挂在环上的两个树的最大深度根之间的距离之和. 所以,先对每个连通块跑一遍,把环上的点找出来,然后对环上每个点跑 ...
随机推荐
- robotframe处理日志中文问题
unicode('${addr1.text}',"utf-8")
- CPP-基础:关于内存分配
1:c中的malloc和c++中的new有什么区别 (1)new.delete 是操作符,可以重载,只能在C++中使用.(2)malloc.free是函数,可以覆盖,C.C++中都可以使用.(3)ne ...
- python Object-Oriented Programming
Python 类的成员.成员修饰符.类的特殊成员. Python 类的成员 类的成员可以分为三大类: 字段.方法和属性. #注:所有成员中,只有普通字段的内容保存对象中,即: #根据此类创建了多少对象 ...
- CF-1099 D. Sum in the tree
CF-1099 D. Sum in the tree 题意:结点序号为 1~n 的一个有根树,根序号为1,每个点有一个权值a[i], 然后定义一s[i]表示从根节点到 结点序号为i的结点的路途上所经过 ...
- solr 单机模式搭建
系统环境:centos 7 安装前准备 安装JDK环境 下载tomcat.solr安装包:solr下载地址:http://archive.apache.org/dist/lucene/solr/ 安装 ...
- 文件操作-touch
本文来给大家介绍另外一个比较常用的命令--touch命令,Linux touch命令 主要用来修改文件或者目录的时间属性,或者建立新文件. 转载自https://www.linuxdaxue.com/ ...
- CSS3的背景background
CSS3中的Background属性 background: background-image || background-position/background-size || background ...
- python面向对象(C3算法)(六)
1. 了解python2和python3类的区别 python2在2.3之前使用的是经典类, 2.3之后, 使用的是新式类 2. 经典类的MRO 树形结构的深度优先遍历 -> 树形结构遍历 cl ...
- (转)python之禅
凡是用过 Python的人,基本上都知道在交互式解释器中输入 import this 就会显示 Tim Peters 的 The Zen of Python,但它那偈语般的语句有点令人费解,所以我想分 ...
- grep理解
http://www.cnblogs.com/ggjucheng/archive/2013/01/13/2856896.html部分摘录于此 grep与正规表达式 字符类 字符类的搜索:如果我想要搜 ...