[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 ...
随机推荐
- js中字符串的方法,17种方法
字符串的17种方法...... 1.length:返回字符串的长度. const str = "Hello, World!"; console.log(str.length); / ...
- Java8 多线程及并行计算demo
Java8 多线程及并行计算demo #接口 public interface RemoteLoader { String load(); default void delay() { try { T ...
- JAVA Stream在jdk17下的例子
最近因为某些原因,又要用stream,所以耗费了一些时间,做了一些例子,以便自己后续参考. 环境: windows11 jdk 17 spring 2.6.7 Article类代码: package ...
- detect.py - yolov5master nvidia jetson agx xavier for mask with UART
import argparse import time from pathlib import Path import cv2 import torch import torch.backends.c ...
- 13-flex
01 flex2个重要的概念 02 flex布局模型 03 flex相关属性 04 flex container相关属性 4.1 flex direction 不同的值会改变主轴的方向 4.2 fle ...
- 在高通Fastmmi模式中增强交互方式
在高通Fastmmi模式中增强交互方式 背景 由于之前工厂抱怨 FCT模式不好用. 之前的FCT测试是这样子的:PCBA上夹具,连接USB. 同时,使用上位机程序(ATE)发送指令,人工判断结果以后, ...
- 一次Java服务内存过高的分析过程
现象 年前,收到了短信报警,显示A服务的某台机器内存过高,超过80% 如上图所示,内存会阶段性增加.奇怪的是,十多台机器中只有这一台有这个问题 堆内内存分析 最先怀疑是内存泄漏的问题,所以首先使用jm ...
- Mac 每次都要执行source ~/.bash_profile 配置的环境变量才生效
自己在 ~/.bash_profile 中配置环境变量, 可是每次重启终端后配置的不生效.需要重新执行 : $source ~/.bash_profile 发现zsh加载的是 ~/.zshrc文件,而 ...
- c++ primer 第五版随笔
1.what is std::endl ? for example, std::cout << "hello world" << std::endl; st ...
- Swift开发基础03-函数
定义 形参默认是let,也只能是let func sum(v1: Int, v2: Int) -> Int { return v1 + v2 } sum(v1: 10, v2: 20) // 无 ...