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]
基环树直径裸题. 首先基环树直径只可能有两种形式:每棵基环树中的环上挂着的树的直径,或者是挂在环上的两个树的最大深度根之间的距离之和. 所以,先对每个连通块跑一遍,把环上的点找出来,然后对环上每个点跑 ...
随机推荐
- [LUOGU] P1048 采药
题目描述 辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师.为此,他想拜附近最有威望的医师为师.医师为了判断他的资质,给他出了一个难题.医师把他带到一个到处都是草药的山洞里对他说:" ...
- <MySQL>入门一 查询 DQL
1. 数据库表 1.1 员工表 Create Table CREATE TABLE `employees` ( `employee_id` ) NOT NULL AUTO_INCREMENT, `fi ...
- 【DB_MySQL】 limit——取查询结果的子集
语法:select * from student limit beginIndex,length; 这里结果集的下标同数组一样从0开始,beginIndex表示起始位置,length表示从beginI ...
- 继上次编译openwrt之后,添加web界面
上编博客写了关于openwrt编译环境和编译一个默认配置的openwrt系统. 现在我正在做如何添加web界面.(hiwooya自带的luci web) 方法如下: 首先在编译环境中配置 make m ...
- Linux文件权限基础(一)
Linux中每个文件或者目录对都有一组共9个基础权限位,没三位字符被分为一组,他们分别是属主权限位,用户组权限位,其他用户权限位. 示例: 权限位说明: r --read 可读权限 对应数字4 w - ...
- leepcode(斐波那契数列与float("inf")无穷大)解析
12.加一 给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一. 最高位数字存放在数组的首位, 数组中每个元素只存储一个数字. 你可以假设除了整数 0 之外,这个整数不会以零开头. 示例 ...
- nw335 debian sid x86-64 -- 4 realtek 提供的官方驱动
realtek 提供的官方驱动 1 查看无线网卡的驱动芯片: $ sudo lsusb Bus 001 Device 003: ID 0bda:8176 Realtek Semiconductor ...
- python基础——11(模块初识)
一.函数回调 # 提前写出函数的调用,再去考虑函数体的实现 # 怎么样提前写出函数的调用:在另一个函数中写出函数的调用 # 再去考虑函数体的实现:根据实际的需求 # 自定义一个sleep def my ...
- django基础(web框架,http协议,django安装)
学习Django之前我们先来看什么是OSI七层模型: 应用层 表示层 应用层(五层模型中把这三层合成一个应用层) http协议 会话层 传输层 提供端口对 ...
- Cocoa-Cocoa框架
1.Cocoa是什么? Cocoa是OS X和 iOS操作系统的程序的运行环境. 是什么因素使一个程序成为Cocoa程序呢?不是编程语言,因为在Cocoa开发中你可以使用各种语言:也不是开发工具,你可 ...