[COCI 2023/2024 #2] Zatopljenje 题解
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 题解的更多相关文章
- 百度之星初赛A 今夕何夕
今夕何夕 今天是2017年8月6日,农历闰六月十五. 小度独自凭栏,望着一轮圆月,发出了"今夕何夕,见此良人"的寂寞感慨. 为了排遣郁结,它决定思考一个数学问题:接下来最近的哪一年 ...
- Bzoj索引
1001 : http://ideone.com/4omPYJ1002 : http://ideone.com/BZr9KF1003 : http://ideone.com/48NJNh1004 : ...
- Hsql中In没有1000的限制
SELECT * FROM user , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ...
- HDU 6112 今夕何夕
今夕何夕 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- 日期求星期(java)-蓝桥杯
日期求星期问题(java)-蓝桥杯 1:基姆拉尔森计算公式(计算星期) 公式: int week = (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400)%7; 此处y,m,d指代年 ...
- 解决Nginx重启时提示nginx: [emerg] bind() to 0.0.0.0:80错误
Nginx是一款轻量级的Web服务器,特点是占有内存少,并发能力强,因而使用比较广泛,蜗牛今天在一个VPS上重启Nginx时提示“nginx: [emerg] bind() to 0.0.0.0:80 ...
- 2017"百度之星"程序设计大赛 - 初赛(A) 01,05,06
小C的倍数问题 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem ...
- HDU 2021 发工资咯:)(最水贪心)
传送门: http://acm.hdu.edu.cn/showproblem.php?pid=2021 发工资咯:) Time Limit: 2000/1000 MS (Java/Others) ...
- 百度之星2017初赛A-1005-今夕何夕
今夕何夕 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- [SinGuLaRiTy] 2017 百度之星程序设计大赛 初赛A
[SinGuLaRiTy-1036] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 小C的倍数问题 Time Limit: 2000/100 ...
随机推荐
- 微信支付or支付宝支付调用流程图
微信支付or支付宝支付调用流程图 支付宝小程序支付调用流程https://opendocs.alipay.com/mini/03l735 微信H5支付调用流程https://pay.weixin.qq ...
- 关于Compilation failed: internal java compiler error的解决方法(Idea)
关于Compilation failed: internal java compiler error的解决方法(Idea) idea编译项目时出现java: Compilation failed: i ...
- 社会工程学——进行IP追踪
如果目标对象有一个公开的邮箱,可以往这个邮箱地址发试探性的Email,然后看看该邮件是否有[回信],从而了解对象是否在线.(注:这招是社会工程学的基本伎俩) 说一个稍微高级点的邮件技巧--[不依赖回信 ...
- UE4打包发布后,在Windows和Android平台上访问非Asset文件
1.问题来源 最近的项目里面有个需求,要在打包之后的exe或者apk运行起来后访问工程Content或者安卓目录下的非Asset文件,比如text文件,json文件等,从中读取一些可随时修改的配置项信 ...
- Android 7 默认声音/大小修改
背景 客户机器默认的开机声音一直很大:客户觉得无法接受,需要改小点. 基于Android 7的代码 前言 一般主要通过系统层来进行修改. 在系统关于音频的有关代码中,定义了两个数组: 注意,这些代码根 ...
- UG 2406 python 二次开发环境配置
UG 2406 python 二次开发环境配置 项目地址 https://gitee.com/unm001/nx2406.git 安装python 安装 python 3.10.11 D:\prog\ ...
- 用const修饰指针
1)常量指针 语法:const 数据类型 *变量名; 不能通过解引用的方法修改内存地址中的值(用原始的变量名是可以修改的). 注意: l 指向的变量(对象)可以改变(之前是指向变量a的,后来可以改为指 ...
- mac 安装mysql5.7.28附安装包
mac 安装mysql教程 下载mysql安装包 百度云盘地址: https://pan.baidu.com/s/1qbF8vtON2sLzNetXCITnSQ 运行安装包 一直下一步即可 配置环境变 ...
- Simple WPF: WPF自定义一个可以定义步长的SpinBox
最新内容优先发布于个人博客:小虎技术分享站,随后逐步搬运到博客园. 通过WPF的按钮.文本输入框实现了一个简单的SpinBox数字输入用户组件并可以通过数据绑定数值和步长.本文中介绍了通过Xaml代码 ...
- Solo 开发者周刊 (第10期):Sora 之后,谁是被遗忘的?谁又是被仰望的?
这里会整合 Solo 社区每周推广内容.产品模块或活动投稿,每周五发布.在这期周刊中,我们将深入探讨开源软件产品的开发旅程,分享来自一线独立开发者的经验和见解.本杂志开源,欢迎投稿. 好文推荐 Sol ...