题目大意:给定一个长度为 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. Linux删除命令rm

    在用Linux的时候,有时分要删除一个文件夹,常常会提示次此文件非空,没法删除,这个时分,必需运用rm -rf命令.关于一些小白用户常常在运用Linux命令,会十分当心,以免搞出一些事情,下面小编将教 ...

  2. NOIp2018D1T1 积木大赛 【思维】

    题目传送门 感觉不是很难,但是需要一些思考... 可以发现,贪心地向尽量大的区间添加,但是存在一些比较小的数,它们不需要再加了,就会从那个地方断成两个区间.所以刚开始想到的做法就是统计每一种数的数量, ...

  3. C++学习笔记-C++对C语言的函数拓展

    内联函数 内联函数是指用inline关键字修饰的函数.在类内定义的函数被默认成内联函数.内联函数从源代码层看,有函数的结构,而在编译后,却不具备函数的性质 inline关键字只是给编译器一个建议,编译 ...

  4. elk 概念整理 集群状态 - yellow - 面试的问题 -- 官方配置文档 水平扩容以及数据保障

    1. primary shard   -- raid0 2.replicas shard -- raid1 3.index -- 图书馆的借书指引 4.MySQL vs elasticsearch # ...

  5. linux运维、架构之路-Lnmp架构部署

    一.Lnmp架构原理 二. Lnmp架构软件安装 1.Nginx安装脚本 #!/bin/bash useradd -s /sbin/nologin -M www mkdir -p /server/to ...

  6. IIS写权限漏洞和XFF刷票原理

    IIS写权限漏洞 PUT写入漏洞 此漏洞主要是因为服务器开启了webdav的组件导致的 1.扫描漏洞,yes,可以PUT: 2.用老兵的工具上传一句话文件test.txt,然后move改名为shell ...

  7. Java基础(八)

    IO流 概述与分类 Java中的IO流技术主要用于传输数据.典型的应用场景有:读写本地文件,上传下载文件等.按照数据传输的方向可以分为两种: l  输入流(Input):既让数据进入应用程序中. l  ...

  8. Java基础(五)

    方法概述 方法的定义格式 什么是方法?方法就好像是一个工厂. 如奶牛场 原料:奶牛,饲料 产出物:牛奶,各种奶制品 程序当中的方法 参数(原料):进入方法的数据 返回值(产出物):从方法中出来的数据 ...

  9. ABC133F Small Products

    考虑 DP. 状态 令 $f[\ell][x]$ 表示长度为 $\ell$,首项不超过 $x$ 的序列的个数. 答案是 $f[K][N]$. 有递推 $f[\ell][x] = f[\ell][x - ...

  10. Centos8 安装ifconfig(net-tools.x86_64)

    情况: yum search net-tools.x86_64 没有找到结果 解决: 1.yum search net-tools 2. yum install -y net-tools.x86_64 ...