题目大意:给定一个长度为 N 的序列,定义连续区间 [l, r] 为:序列的一段子区间,满足 [l, r] 中的元素从小到大排序后,任意相邻两项的差值不超过1。求一共有多少个连续区间。

题解:单调栈 + 线段树

首先,对于区间计数类问题常规的思路是枚举区间的左端点或右端点,统计以该点为端点的区间个数,加入答案贡献。

对于这道题来说,不妨枚举答案的右端点 r,那么对于每个 r,需要快速得出有多少个左端点 l,使得区间 [l, r] 满足连续区间的性质。若能在 \(O(logn)\) 的时间内得出答案即可解决本题。

根据连续区间的性质,可知连续区间的定义等价于

\[max(a[l...r])-min(a[l...r])+1 \ge cnt
\]

其中,cnt 为区间 [l, r] 中不同数字的个数。可以发现,只有取得等号的时候才满足连续区间的性质,即:\(max - min - cnt = -1\)。因此,对于每个枚举到的右端点 r,我们需要知道每个小于 r 的 l, [l, r] 区间的最大值和最小值以及区间不同数的个数。

可以利用线段树维护 \(max - min - cnt\),只需维护区间最小值以及区间最小值的个数,即可在线段树上快速回答询问。

维护区间最值可以利用单调栈,即:第 i 个元素入栈时,栈内元素由于单调性,自然维护了区间[i, r] 的最值,每次从栈中弹出元素时,需要在线段树上修改维护的最值贡献。

维护区间颜色数是一个经典问题,即:维护一个 pre 数组,用于记录上一次某个元素出现的位置。

代码如下

#include <bits/stdc++.h>
using namespace std;
typedef long long LL; struct node { // max - min - cnt >= -1
#define ls(o) t[o].lc
#define rs(o) t[o].rc
int lc, rc;
LL mi, cnt, add;
};
vector<node> t;
int tot, rt;
inline void up(int o) {
if (t[ls(o)].mi == t[rs(o)].mi) {
t[o].mi = t[ls(o)].mi;
t[o].cnt = t[ls(o)].cnt + t[rs(o)].cnt;
} else if (t[ls(o)].mi < t[rs(o)].mi) {
t[o].mi = t[ls(o)].mi;
t[o].cnt = t[ls(o)].cnt;
} else {
t[o].mi = t[rs(o)].mi;
t[o].cnt = t[rs(o)].cnt;
}
}
inline void down(int o) {
if (t[o].add != 0) {
t[ls(o)].mi += t[o].add, t[ls(o)].add += t[o].add;
t[rs(o)].mi += t[o].add, t[rs(o)].add += t[o].add;
t[o].add = 0;
}
}
inline int newnode() {
++tot;
t[tot].lc = t[tot].lc = t[tot].mi = t[tot].cnt = t[tot].add = 0;
return tot;
}
void build(int &o, int l, int r) {
o = newnode();
if (l == r) {
t[o].mi = t[o].add = 0, t[o].cnt = 1;
return;
}
int mid = l + r >> 1;
build(ls(o), l, mid);
build(rs(o), mid + 1, r);
up(o);
}
void modify(int o, int l, int r, int x, int y, LL add) {
if (l == x && r == y) {
t[o].mi += add, t[o].add += add;
return;
}
int mid = l + r >> 1;
down(o);
if (y <= mid) {
modify(ls(o), l, mid, x, y, add);
} else if (x > mid) {
modify(rs(o), mid + 1, r, x, y, add);
} else {
modify(ls(o), l, mid, x, mid, add);
modify(rs(o), mid + 1, r, mid + 1, y, add);
}
up(o);
} int main() {
int T, kase = 0;
scanf("%d", &T);
while (T--) {
int n;
scanf("%d", &n);
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
t.resize(2 * n), tot = 0;
build(rt, 1, n);
vector<pair<int, int>> mi(n + 1), mx(n + 1);
int top1 = 0, top2 = 0;
map<int, int> pre;
LL ans = 0;
for (int i = 1, now; i <= n; i++) { // <val, pos>
now = i;
while (top1 > 0 && a[i] < mi[top1].first) {
int pos = mi[top1 - 1].second;
modify(rt, 1, n, pos + 1, now - 1, mi[top1].first - a[i]);
--top1;
now = pos + 1;
}
mi[++top1] = make_pair(a[i], i);
now = i;
while (top2 > 0 && a[i] > mx[top2].first) {
int pos = mx[top2 - 1].second;
modify(rt, 1, n, pos + 1, now - 1, a[i] - mx[top2].first);
--top2;
now = pos + 1;
}
mx[++top2] = make_pair(a[i], i);
if (pre.find(a[i]) != pre.end()) {
int pos = pre[a[i]];
modify(rt, 1, n, pos + 1, i, -1);
} else {
modify(rt, 1, n, 1, i, -1);
}
pre[a[i]] = i;
if (t[rt].mi == -1) {
ans += t[rt].cnt;
}
}
printf("Case #%d: %lld\n", ++kase, ans);
}
return 0;
}

【2019银川网络赛】L:Continuous Intervals的更多相关文章

  1. 2019南昌网络赛I:Yukino With Subinterval(CDQ) (树状数组套主席树)

    题意:询问区间有多少个连续的段,而且这段的颜色在[L,R]才算贡献,每段贡献是1. 有单点修改和区间查询. 思路:46min交了第一发树套树,T了. 稍加优化多交几次就过了. 不难想到,除了L这个点, ...

  2. ACM-ICPC 2019南昌网络赛I题 Yukino With Subinterval

    ACM-ICPC 2019南昌网络赛I题 Yukino With Subinterval 题目大意:给一个长度为n,值域为[1, n]的序列{a},要求支持m次操作: 单点修改 1 pos val 询 ...

  3. ICPC 2019 徐州网络赛

    ICPC 2019 徐州网络赛 比赛时间:2019.9.7 比赛链接:The Preliminary Contest for ICPC Asia Xuzhou 2019 赛后的经验总结 // 比赛完才 ...

  4. 2018宁夏邀请赛 L Continuous Intervals(单调栈+线段树)

    2018宁夏邀请赛 L Continuous Intervals(单调栈+线段树) 传送门:https://nanti.jisuanke.com/t/41296 题意: 给一个数列A 问在数列A中有多 ...

  5. ACM-ICPC 2019南昌网络赛F题 Megumi With String

    ACM-ICPC 南昌网络赛F题 Megumi With String 题目描述 给一个长度为\(l\)的字符串\(S\),和关于\(x\)的\(k\)次多项式\(G[x]\).当一个字符串\(str ...

  6. 2019 南京网络赛A

    南京网络赛自闭现场 https://nanti.jisuanke.com/t/41298 二维偏序经典题型 二维前缀和!!! #include<bits/stdc++.h> using n ...

  7. 2018ICPC银川 L Continuous Intervals 单调栈 线段树

    题意:给你一个序列,问你这个序列有多少个子区间,满足把区间里的数排序之后相邻两个数之间的差 <= 1 ? 思路:https://blog.csdn.net/u013534123/article/ ...

  8. 2019 ICPC 银川网络赛 D. Take Your Seat (疯子坐飞机问题)

    Duha decided to have a trip to Singapore by plane. The airplane had nn seats numbered from 11 to nn, ...

  9. 2019 ICPC 银川网络赛 H. Fight Against Monsters

    It is my great honour to introduce myself to you here. My name is Aloysius Benjy Cobweb Dartagnan Eg ...

随机推荐

  1. 组件推荐Forloop.HtmlHelpers 用来实现MVC的js加载顺序

    最近在开发的时候遇到js加载顺序的问题,layui在底部声明了js,但是我想在页面其他地方使用分布视图,分布视图内有自己的js逻辑,发现不能执行,一看就发现,这里的js应该加在layui后面执行才能有 ...

  2. spring boot密码管理

    1.引入依赖 <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId> ...

  3. 前端vscode常用插件

    Auto Rename Tag 这是一个html标签的插件,可以让你修改一边标签,另外一边自动改变. Beautify 格式化代码插件 Braket Pair Colorizer 给js文件中的每一个 ...

  4. 允许Root登录Docker ubuntu(MAC主机,使用portainer管理)

    1. 完成ubuntu容器的建立,完成ssh端口映射 2. 进入portainer内容器的console界面完成如下修改 a 刷新安装源:apt-get update b 安装sshd:apt-get ...

  5. OpenStack组件——Glance镜像服务

    1.glance介绍 Glance是Openstack项目中负责镜像管理的模块,其功能包括虚拟机镜像的查找.注册和检索等. Glance提供Restful API可以查询虚拟机镜像的metadata及 ...

  6. ssh隧道详解与案例利用讲解

    原文阅读:https://www.52stu.org/?p=206 你是否有过自己身处内网,却想整个远控上线?你是否有过身处目标内网,却想把目标的内网的Linux的ssh转发出来?你是否想过穿透多层网 ...

  7. 页面可见性改变事件 : visibilitychange 详解

    1.Page Visibility API标准概述 查看W3C的官方文档时候看到这个属性 标准时间线是这样介绍的: Page Visibility Level 2 W3C Proposed Recom ...

  8. java.io.IOException 断开的管道 解决方法 ClientAbortException: java.io.IOException: Broken pipe

    今天公司技术支持的童鞋报告一个客户的服务不工作了,紧急求助,于是远程登陆上服务器排查问题. 查看采集数据的tomcat日志,习惯性的先翻到日志的最后去查看有没有异常的打印,果然发现了好几种异常信息,但 ...

  9. MSF魔鬼训练营-3.4.2网络漏洞扫描-OpenVAS(待补完)PS:在虚拟机里面运行OpenVAS扫描的进度真的是超慢啊...

     由于新版的kali中没有预装OpenVAS.所以在虚拟机中安装花了非常多的时间. 安装过程参考:http://www.cnblogs.com/zlslch/p/6872559.html过程写的非常详 ...

  10. 浅谈Linux cp命令

    Linux 的cp命令 功能: 复制文件或目录说明: cp指令用于复制文件或目录,如同时指定两个以上的文件或目录,且最后的目的地是一个已经存在的目录,则它会把前面指定的所有文件或目录复制到此目录中.若 ...