[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 ...
随机推荐
- Java格式化手机号和身份证号,中间使用星号*隐藏
Java格式化手机号和身份证号,中间使用星号*隐藏 package com.example.core.mydemo.java; /** * renterMobile=111****1198 * idN ...
- xxlJob Cron表达式 0 0 8,13 * * ?
xxlJob Cron表达式 0 0 8,13 * * ? Cron有如下两种语法格式:(1)Seconds Minutes Hours DayofMonth Month DayofWeek Yea ...
- 5分钟理透LangChain的Chain
LangChain几乎是LLM应用开发的第一选择,它的野心也比较大,它致力于将自己打造成LLM应用开发的最大社区.而LangChain最核心的部分非 Chain 莫属. 那Chain到底是个啥,概念比 ...
- pytest-parallel 多线程执行用例,访问pymysql数据库,遇到的问题
遇到的问题 1.pymysql.err.ProgrammingError: execute() first 2.File "D:\xxx\python3.8\lib\site-package ...
- 13-flex
01 flex2个重要的概念 02 flex布局模型 03 flex相关属性 04 flex container相关属性 4.1 flex direction 不同的值会改变主轴的方向 4.2 fle ...
- Web运作原理探析
Web运作原理探析 1.1 web的 概念 Web是一种分布式的应用架构,旨在共享分布在网络上的各个Web服务器中的所有互相链接的信息. 1.2 HTML是指超文本标记语言. 1.3 URL简介 UR ...
- Xilinux PS与PL交互::Linux-App读写REG
Xilinux PS与PL交互::Linux-App读写REG 背景 PL配置好有关的硬件,PS端做验证. 设计方案:针对REG地址,不使用设备树配置. 遇到的问题:暂无. 验证目的 验证PL-PS的 ...
- Java模拟高并发测试
线程类,设置有一个公共资源 package cn.org.chris.concurrent; import java.util.concurrent.locks.Lock; import java.u ...
- Power BI实用技巧:轻松打造专业级甘特图
Power BI实用技巧:轻松打造专业级甘特图 大家好,今天我们要一起探索Power BI中一个既实用又强大的功能--制作甘特图.甘特图以其直观展示项目时间线和任务进度的特点,在项目管理中扮演着重要角 ...
- 计算订单签收率的sql查询思路与过程(涉及百分比和四舍五入)
领导提出一个签收率需求,想要通过数据库达到excel中表现的形式,提高计算速度和工作效率, 如下形式: 数据库中表数据结构: 部分数据如下: sql语句思路如下: -- 1.已签收:以物流反馈管道,状 ...