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]
基环树直径裸题. 首先基环树直径只可能有两种形式:每棵基环树中的环上挂着的树的直径,或者是挂在环上的两个树的最大深度根之间的距离之和. 所以,先对每个连通块跑一遍,把环上的点找出来,然后对环上每个点跑 ...
随机推荐
- jQuery和CSS的拍摄效果
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Philipp Wagner
本文大部分来自OpenCV官网上的Face Reconition with OpenCV这节内容(http://docs.opencv.org/modules/contrib/doc/facerec/ ...
- Spring根据XML配置文件注入属性 其实也是造bean,看看是使用constructor还是setter顺带完成属性赋值
方法一使用setter方法 package com.swift; public class Book { private String bookName; public void setBook(St ...
- ios之UIWebView(2)
UIWebView是iOS sdk中一个最常用的控件.是内置的浏览器控件,我们可以用它来浏览网页.打开文档等等.这篇文章我将使用这个控件,做一个简易的浏览器.如下图: 我们创建一个Window-bas ...
- 洛谷 P1663 山
https://www.luogu.org/problemnew/show/P1663 可能在这里看会好一点:[题解]
- C++模板 · 为什么要引入模板机制?
刚学过类模板时,很不理解,甚至觉得这简直没有用,在自己骗自己嘛!明明很方便的东西,偏偏要加个类模板来回折腾.可能因为我们刚开始写的程序很简单,有时候,可能程序复杂一点,对理解一些概念更有帮助. 今天在 ...
- (34)zabbix Queue队列
概述 queue(队列)显示监控项等待刷新的时间,可以看到每种agent类型刷新时间,通过queue可以更好的体现出监控的一个指标.正常情况下,是一片绿色. 如果出现过多红色,那么需要留意一下.我们也 ...
- (20)zabbix触发器triggers
触发器是什么 触发器(triggers)是什么?触发器使用逻辑表达式来评估通过item获取到得数据是处于哪种状态,item一收回数据,讲解任务交给触发器去评估状态,明白触发器是怎么一回事了把?在触发器 ...
- 微信小程序 wx.request POST请求------中文乱码问题
问题: 一个简单的表单,提交后台返回数据“提交成功”. 以为没问题了,但是没过多久后台小哥就问为啥那么多乱码,找了很久原因,发现在提交的时候就已经乱码了. 嗯,前端问题,然后测试GET/POST方法. ...
- LeetCode(8)String to Integer (atoi)
题目: Implement atoi to convert a string to an integer. Hint: Carefully consider all possible input ca ...