UPDATE on 2024.4.25

改掉奇怪压行码风,并稍作排版。

前言

题目链接:洛谷

题目分析

首先发现区间中的个数等于 \(\texttt{高度大于 x 的位置的个数} - \texttt{连续两个位置都是大于 x 的位置的个数}\)。具体令 \(H_i = \min(h_i, h_{i+1}) (i \in [1, n-1])\),那么对于一次询问答案 \(ans=\sum\limits_{i=l}^{r}[h_i > x] - \sum\limits_{i=l}^{r-1}[H_i > x]\),其中 \([a > b]\) 表示当 \(a > b\) 时为 \(1\),反之为 \(0\)。特别地,对于 \(l=r\) 的情况,答案就是 \([h_i > x]\)。

证明:一个岛屿一定是由一段连续的高度大于 \(x\) 的位置组成,那么这一段连续两个位置都是大于 \(x\) 的位置的个数正好是这一段区间的长度减一(可以想象这一段区间有多少个间隔,显然是区间长度减一),那么两者相减就是 \(1\) ,求和之后就算出了 \(1\) 的个数,也就是岛屿的个数。证毕。

很明显,原问题成了两个数组中区间内大于某个数的个数,具体来讲,就是 \(h_l \sim h_r\) 大于 \(x\) 的个数减去 \(H_l \sim H_{r-1}\) 大于 \(x\) 的个数。使用分块是个做法,但是由于 不能过这道题 我们要学习更优的 \(\log\) 的算法,有以下两种解法。

离线使用线段树

考虑对原问题离线,将 \([l, r]\) 的询问拆成 \([1, r]\) 的询问减去 \([1, l-1]\) 的询问,这样从左到右扫一遍,同时维护一个数据结构能快速求得目前比 \(x\) 大的个数。离散化使用树状数组是一种方法,或者直接使用权值线段树。

时间复杂度为 \(\Theta((n+q)\log n)\) 以及一个线段树不小的常数? 。

直接使用主席树

其实可以考虑使用可持久化的数据结构(主席树),这样可以在线解决,万一题目强制在线,那么离线的全都挂掉了。剩下的就是板子了。具体地,我们讲所有高度离散化,主席树里面存值域区间出现的数字个数。对于询问,我们在离散化之前所有高度中找到最后一个小于等于 \(x\) 的高度(由于离散化要先排序,这一步可以用 upper_bound 二分,对时间复杂度没有影响),令这个高度为 \(h_0\),那么就查询 \(l \sim r\) 中大于 \(h_0\) 的个数即可。

时间复杂度:\(\Theta((n+q)\log n)\)。

代码

离线使用线段树

#include <cstdio>
#include <vector>
using namespace std; int n, q, h[200010], H[200010], ans[200010]; struct node{
int f, idx, x;
};
vector<node> qry1[200010], qry2[200010]; const int inf = 1000000000; struct Segment_Tree{
struct node{
int lson, rson, sum;
} tree[1000010 << 2];
int tot, root;
void pushup(int idx){
tree[idx].sum = tree[tree[idx].lson].sum + tree[tree[idx].rson].sum;
}
int query(int &idx, int trl, int trr, int l, int r){
if (!idx || trl > r || trr < l) return 0;
if (l <= trl && trr <= r) return tree[idx].sum;
int mid = (trl + trr) >> 1;
return query(tree[idx].lson, trl, mid, l, r) + query(tree[idx].rson, mid + 1, trr, l, r);
}
void modify(int &idx, int trl, int trr, int p, int v){
if (trl > p || trr < p) return;
if (!idx) idx = ++tot, tree[idx] = {0, 0, 0};
if (trl == trr) return tree[idx].sum += v, void();
int mid = (trl + trr) >> 1;
modify(tree[idx].lson, trl, mid, p, v), modify(tree[idx].rson, mid + 1, trr, p, v), pushup(idx);
}
void clear(){ tot = 0, root = 0; }
} yzh; signed main(){
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; ++i) scanf("%d", &h[i]), H[i-1] = min(h[i-1], h[i]);
for (int i = 1, l, r, x; i <= q; ++i){
scanf("%d%d%d", &l, &r, &x);
if (l == r) ans[i] = h[l] > x;
else {
qry1[r].push_back({1, i, x}), qry1[l - 1].push_back({-1, i, x});
qry2[l - 1].push_back({1, i, x}), qry2[r - 1].push_back({-1, i, x});
}
}
for (int i = 1; i <= n; ++i){
yzh.modify(yzh.root, 0, inf, h[i], 1);
for (const auto & [f, idx, x]: qry1[i])
ans[idx] += f * yzh.query(yzh.root, 0, inf, x + 1, inf);
}
yzh.clear();
for (int i = 1; i <= n - 1; ++i){
yzh.modify(yzh.root, 0, inf, H[i], 1);
for (const auto & [f, idx, x]: qry2[i])
ans[idx] += f * yzh.query(yzh.root, 0, inf, x + 1, inf);
}
for (int i = 1; i <= q; ++i) printf("%d\n", ans[i]);
return 0;
}

直接使用主席树

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std; int n, q, h[200010], real[200010], V; struct Yzh_Is_The_President{
struct node{
int lson, rson;
int val;
} tree[200010 * 24];
int root[200010], tot;
void pushup(int idx){
tree[idx].val = tree[tree[idx].lson].val + tree[tree[idx].rson].val;
}
void build(int &idx, int l, int r){
tree[idx = ++tot] = {0, 0, 0};
if (l == r) return;
int mid = (l + r) >> 1;
build(tree[idx].lson, l, mid), build(tree[idx].rson, mid + 1, r);
}
void modify(int &idx, int oidx, int trl, int trr, int p, int v){
if (trl > p || trr < p) return;
tree[idx = ++tot] = tree[oidx];
if (trl == trr) return tree[idx].val += v, void();
int mid = (trl + trr) >> 1;
modify(tree[idx].lson, tree[oidx].lson, trl, mid, p, v);
modify(tree[idx].rson, tree[oidx].rson, mid + 1, trr, p, v);
pushup(idx);
}
int query(int lidx, int ridx, int trl, int trr, int l, int r){
if (trl > r || trr < l) return 0;
if (l <= trl && trr <= r) return tree[ridx].val - tree[lidx].val;
int mid = (trl + trr) >> 1;
return query(tree[lidx].lson, tree[ridx].lson, trl, mid, l, r) +
query(tree[lidx].rson, tree[ridx].rson, mid + 1, trr, l, r);
}
} xym, yzh; signed main() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; ++i) scanf("%d", &h[i]), real[i] = h[i];
sort(real + 1, real + n + 1);
V = unique(real + 1, real + n + 1) - real - 1;
for (int i = 1; i <= n; ++i) h[i] = lower_bound(real + 1, real + V + 1, h[i]) - real; xym.build(xym.root[0], 1, V), yzh.build(yzh.root[0], 1, V);
for (int i = 1; i <= n; ++i) xym.modify(xym.root[i], xym.root[i - 1], 1, V, h[i], 1);
for (int i = 1; i <= n - 1; ++i) yzh.modify(yzh.root[i], yzh.root[i - 1], 1, V, min(h[i], h[i + 1]), 1); for (int i = 1; i <= q; ++i) {
int l, r, x;
scanf("%d%d%d", &l, &r, &x);
if (l == r)
printf("%d\n", real[h[l]] > x);
else if (x < real[1])
puts("1");
else {
int v = upper_bound(real + 1, real + V + 1, x) - real - 1;
printf("%d\n", xym.query(xym.root[l - 1], xym.root[r], 1, V, v + 1, V
- yzh.query(yzh.root[l - 1], yzh.root[r - 1], 1, V, v + 1, V));
}
}
return 0;
}

[COCI 2023/2024 #2] Zatopljenje 题解的更多相关文章

  1. 百度之星初赛A 今夕何夕

    今夕何夕 今天是2017年8月6日,农历闰六月十五. 小度独自凭栏,望着一轮圆月,发出了"今夕何夕,见此良人"的寂寞感慨. 为了排遣郁结,它决定思考一个数学问题:接下来最近的哪一年 ...

  2. Bzoj索引

    1001 : http://ideone.com/4omPYJ1002 : http://ideone.com/BZr9KF1003 : http://ideone.com/48NJNh1004 : ...

  3. Hsql中In没有1000的限制

    SELECT * FROM user , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ...

  4. HDU 6112 今夕何夕

    今夕何夕 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  5. 日期求星期(java)-蓝桥杯

    日期求星期问题(java)-蓝桥杯 1:基姆拉尔森计算公式(计算星期) 公式: int week = (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400)%7; 此处y,m,d指代年 ...

  6. 解决Nginx重启时提示nginx: [emerg] bind() to 0.0.0.0:80错误

    Nginx是一款轻量级的Web服务器,特点是占有内存少,并发能力强,因而使用比较广泛,蜗牛今天在一个VPS上重启Nginx时提示“nginx: [emerg] bind() to 0.0.0.0:80 ...

  7. 2017"百度之星"程序设计大赛 - 初赛(A) 01,05,06

    小C的倍数问题    Time Limit: 2000/1000 MS (Java/Others)  Memory Limit: 32768/32768 K (Java/Others) Problem ...

  8. HDU 2021 发工资咯:)(最水贪心)

    传送门: http://acm.hdu.edu.cn/showproblem.php?pid=2021 发工资咯:) Time Limit: 2000/1000 MS (Java/Others)    ...

  9. 百度之星2017初赛A-1005-今夕何夕

    今夕何夕 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  10. [SinGuLaRiTy] 2017 百度之星程序设计大赛 初赛A

    [SinGuLaRiTy-1036] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 小C的倍数问题 Time Limit: 2000/100 ...

随机推荐

  1. base64图片文件上传OSS,下载OSS图片转换为InputStream,文件转base64,base64转文件工具类

    base64图片文件上传OSS,下载OSS图片转换为InputStream,文件转base64,base64转文件工具类 OSSUtils.java public class OSSUtils { p ...

  2. springboot项目编译时,使用自定义注解类找不到符号

    springboot项目编译时,使用自定义注解类找不到符号 Java项目编译时,使用自定义注解类找不到符号Spring-boot项目编辑器:idea问题:编译时找不到符号.项目中用到了自定义注解类.编 ...

  3. error while loading shared libraries: liblzma.so.5: cannot open shared object file: No such file or directory

    CentOS6安装mongo报错 error while loading shared libraries: liblzma.so.5: cannot open shared object file: ...

  4. 12-Python数据库访问

    在CentOS7上安装Mariadb https://blog.csdn.net/NetRookieX/article/details/104734181 常用的增删改查 show databases ...

  5. Web运作原理探析

    Web运作原理探析 1.1 web的 概念 Web是一种分布式的应用架构,旨在共享分布在网络上的各个Web服务器中的所有互相链接的信息. 1.2 HTML是指超文本标记语言. 1.3 URL简介 UR ...

  6. opengrok源代码在线阅读平台搭建及字体修改

    服务搭建 我所编写的docker-compose.yml如下,成功运行后将源码目录移动至 /data/opengrok/src ,重启容器使得opengrok快速更新索引 services: open ...

  7. 『vulnhub系列』doubletrouble-1

    『vulnhub系列』doubletrouble-1 下载地址 https://www.vulnhub.com/entry/doubletrouble-1,743/ 信息搜集 使用命令,获得存活靶机I ...

  8. VS License Header Manager 插件

    /************************************************************************************* * * 文 件 名: %F ...

  9. Spring声明事务和分布式事务处理技术

    Spring声明事务的两种方式 方式一.传统的编程式事务管理: 需要手动编写代码在业务层注入事务管理模板(一般不用) 方式二.基于 AOP 技术实现的声明式事务管理: Spring 声明式事务管理在底 ...

  10. Mac制作U盘启动项

    导读 鄙人刚买回来的电脑,自带系统版本:10.14.5(19款的),有一天,提示系统升级,升到了10.15.4,从此落下了后遗症,mac系统密码输入完之后,读条读到2/3的时候,会黑屏闪一下,百思不得 ...